From: Patrick Brady Date: Fri, 18 Oct 2019 18:50:47 +0000 (-0700) Subject: Revert "remove redundant code under appc-common-bundle/java" X-Git-Tag: 1.7.0~30 X-Git-Url: https://gerrit.onap.org/r/gitweb?a=commitdiff_plain;ds=sidebyside;h=ff3fa00b69b090f156fffa5ef5f8486624321a94;p=appc.git Revert "remove redundant code under appc-common-bundle/java" Testing to see if this change is causing probels with sonar coverage. This reverts commit 3533ff6d195b9966cbf407238bb5d2fd7ca68bb1. Change-Id: I986f27b3ca80b9e8db6544f17fdf3086152d1701 Signed-off-by: Patrick Brady Issue-ID: APPC-1732 --- diff --git a/appc-core/appc-common-bundle/java/org/onap/appc/CmdLine.java b/appc-core/appc-common-bundle/java/org/onap/appc/CmdLine.java new file mode 100644 index 000000000..383de8949 --- /dev/null +++ b/appc-core/appc-common-bundle/java/org/onap/appc/CmdLine.java @@ -0,0 +1,55 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * 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. + * + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + * ============LICENSE_END========================================================= + */ + + package org.onap.appc; + +import org.onap.appc.encryption.EncryptionTool; + +public class CmdLine { + + public static void main(String[] args) { + + if (args.length < 1) { + printUsage(); + return; + } + + String command = args[0];//first parameter + + if (0 == command.compareTo("encrypt") && args.length == 2)//two parameters are required + { + String clearText = args[1]; + String encrypted = EncryptionTool.getInstance().encrypt(clearText); + System.out.println(encrypted); + return; + } else { + printUsage(); + } + } + + private static void printUsage(){ + System.out.println("Usage: java -jar ..."); + System.out.println("\tencrypt \t\t(Encrypts your text)"); + } +} \ No newline at end of file diff --git a/appc-core/appc-common-bundle/java/org/onap/appc/Constants.java b/appc-core/appc-common-bundle/java/org/onap/appc/Constants.java new file mode 100644 index 000000000..c1a306a8c --- /dev/null +++ b/appc-core/appc-common-bundle/java/org/onap/appc/Constants.java @@ -0,0 +1,209 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * 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.appc; + +/** + * This class contains the definitions of all constant values used in the APPC provider, adapters, and other components. + * These constants define properties, settings, and context variables. The context variables can be referenced from + * within the directed graph(s) to access information placed their by the provider and adapters. + *

+ * Context properties are set in the graph context by the various adapters and the provider, or by the graph itself. + * These properties may also be accessed by the graph, adapters, or the provider. It is these properties that allow + * communication of state through the directed graph. All context properties have a symbolic name that starts with + * "CONTEXT_". + *

+ * + */ + +public final class Constants { + + /** + * The name for the error code attribute to be set in the context + */ + @SuppressWarnings("nls") + public static final String ATTRIBUTE_ERROR_CODE = "error_code"; + + /** + * The name for the error message attribute to be set in the context + */ + @SuppressWarnings("nls") + public static final String ATTRIBUTE_ERROR_MESSAGE = "error-message"; + + /** + * The name for the success message attribute to be set in the context + */ + @SuppressWarnings("nls") + public static final String ATTRIBUTE_SUCCESS_MESSAGE = "success-message"; + + public static final String DG_ATTRIBUTE_STATUS = "SvcLogic.status"; + public static final String DG_OUTPUT_STATUS_CODE = "output.status.code"; + public static final String DG_OUTPUT_STATUS_MESSAGE = "output.status.message"; + + /** + * The property that defines the name of the DG service logic to be loaded + */ + public static final String PROPERTY_MODULE_NAME = "appc.service.logic.module.name"; + + /** + * The property that defines the topology restart DG version to be used + */ + public static final String PROPERTY_TOPOLOGY_VERSION = "appc.topology.dg.version"; + + /** + * The method name of the DG that is used to perform topology restart operations + */ + public static final String PROPERTY_TOPOLOGY_METHOD = "appc.topology.dg.method"; + + /** + * The property that supplies the application name + */ + public static final String PROPERTY_APPLICATION_NAME = "appc.application.name"; + + /** + * The execution mode for the directed graph + */ + public static final String SYNC_MODE = "sync"; + + /** + * The name of the property that contains the service request enumerated value in the graph's context + */ + public static final String CONTEXT_SERVICE = "org.onap.appc.service"; + + /** + * The name of the property that contains the VM id value in the graph's context + */ + public static final String CONTEXT_VMID = "org.onap.appc.vmid"; + + /** + * The name of the property that contains the VM id value in the graph's context + */ + public static final String CONTEXT_IDENTITY_URL = "org.onap.appc.identity.url"; + + /** + * The name of the property that contains the service request id value in the graph's context + */ + public static final String CONTEXT_REQID = "org.onap.appc.reqid"; + + /** + * The name of the property that indicates which method of the IaaS adapter to call + */ + public static final String CONTEXT_ACTION = "org.onap.appc.action"; + + /** + * The enumerated value for restart of a VM. This is a constant for one possible value of CONTEXT_SERVICE. + */ + public static final String SERVICE_RESTART = "RESTART"; + + /** + * The enumerated value for rebuild of a VM. This is a constant for one possible value of CONTEXT_SERVICE. + */ + public static final String SERVICE_REBUILD = "REBUILD"; + + /** + * The name of the adapter. We get the name from a property file so that it can be changed easily if needed. + */ + public static final String PROPERTY_ADAPTER_NAME = "org.onap.appc.provider.adaptor.name"; + + /** + * The minimum number of contexts to cache in each provider/tenant pool + */ + public static final String PROPERTY_MIN_POOL_SIZE = "org.onap.appc.provider.min.pool"; + + /** + * The maximum number of contexts to cache in each provider/tenant pool + */ + public static final String PROPERTY_MAX_POOL_SIZE = "org.onap.appc.provider.max.pool"; + + /** + * The amount of time, in seconds, that the application waits for a change of state of a server to a known valid + * state before giving up and failing the request. + */ + public static final String PROPERTY_SERVER_STATE_CHANGE_TIMEOUT = "org.onap.appc.server.state.change.timeout"; + + /** + * The amount of time, in seconds, between subsequent polls to the openstack provider to update the state of a + * resource + */ + public static final String PROPERTY_OPENSTACK_POLL_INTERVAL = "org.onap.appc.openstack.poll.interval"; + + /** + * The amount of time, in seconds, to wait between retry attempts when a connection to a provider fails. + */ + public static final String PROPERTY_RETRY_DELAY = "org.onap.appc.provider.retry.delay"; + + /** + * The maximum number of times a connection retry will be attempted before the application fails the request + */ + public static final String PROPERTY_RETRY_LIMIT = "org.onap.appc.provider.retry.limit"; + /** + * The amount of time, in seconds, that the application waits for a change of state of a stacj to a known valid + * state before giving up and failing the request. + */ + public static final String PROPERTY_STACK_STATE_CHANGE_TIMEOUT ="org.onap.appc.stack.state.change.timeout" ; + + @SuppressWarnings("nls") + public static final String STATUS_GETTER = "status-getter"; + + @SuppressWarnings("nls") + public static final String VM_FUSION_STATUS_GETTER = "fusion-vm-status-getter"; + + /** + * The name for the status vm attribute to be set in the context when executing a vmstatuscheck. + */ + @SuppressWarnings("nls") + public static final String STATUS_OF_VM = "status-vm"; + + /** + * Yang revision value to be used while generating YANG module + */ + public static final String YANG_REVISION = "2017-03-03"; + /** + * Yang revision format to be used while formatting YANG revision date + */ + public static final String YANG_REVISION_FORMAT = "YYYY-MM-DD"; + + /** + * Base container for yang that is generated to store in MD-SAL datastore + */ + public static final String YANG_BASE_CONTAINER = "vnf-config-repo"; + + /** + *VNF config list for yang that is generated to store in MD-SAL datastore + */ + public static final String YANG_VNF_CONFIG_LIST = "vnf-config-list"; + + /** + *Base container of VNF configuration data for yang that is generated to store in MD-SAL datastore + */ + public static final String YANG_VNF_CONFIG = "vnf-config"; + + /** + * default constructor prevents instantiation + */ + Constants() { + throw new IllegalAccessError("Constants"); + } +} diff --git a/appc-core/appc-common-bundle/java/org/onap/appc/cache/CacheStrategies.java b/appc-core/appc-common-bundle/java/org/onap/appc/cache/CacheStrategies.java new file mode 100644 index 000000000..12f36863e --- /dev/null +++ b/appc-core/appc-common-bundle/java/org/onap/appc/cache/CacheStrategies.java @@ -0,0 +1,31 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * 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.appc.cache; + +/** + * Enum of CacheStrategies + */ +public enum CacheStrategies { + LRU +} diff --git a/appc-core/appc-common-bundle/java/org/onap/appc/cache/CacheStrategy.java b/appc-core/appc-common-bundle/java/org/onap/appc/cache/CacheStrategy.java new file mode 100644 index 000000000..8976a2075 --- /dev/null +++ b/appc-core/appc-common-bundle/java/org/onap/appc/cache/CacheStrategy.java @@ -0,0 +1,45 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * 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.appc.cache; + +/** + * Interface of CacheStrategy + * @param key + * @param value + */ +public interface CacheStrategy { + /** + * Get object + * @param key of the object + * @return value of the object + */ + V getObject(K key); + + /** + * Put object + * @param key of the object + * @param value of the object + */ + void putObject(K key,V value); +} diff --git a/appc-core/appc-common-bundle/java/org/onap/appc/cache/MetadataCache.java b/appc-core/appc-common-bundle/java/org/onap/appc/cache/MetadataCache.java new file mode 100644 index 000000000..caf319a60 --- /dev/null +++ b/appc-core/appc-common-bundle/java/org/onap/appc/cache/MetadataCache.java @@ -0,0 +1,45 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * 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.appc.cache; + +/** + * Interface of MetadataCache + * @param key + * @param value + */ +public interface MetadataCache { + /** + * Get object + * @param key of the object + * @return value of the object + */ + V getObject(K key); + + /** + * Put object + * @param key of the object + * @param value of the object + */ + void putObject(K key,V value); +} diff --git a/appc-core/appc-common-bundle/java/org/onap/appc/cache/impl/LRUCache.java b/appc-core/appc-common-bundle/java/org/onap/appc/cache/impl/LRUCache.java new file mode 100644 index 000000000..c88439c32 --- /dev/null +++ b/appc-core/appc-common-bundle/java/org/onap/appc/cache/impl/LRUCache.java @@ -0,0 +1,58 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * 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.appc.cache.impl; + +import java.util.LinkedHashMap; +import java.util.Map; + +import org.onap.appc.cache.CacheStrategy; + +/** + * LRU cache implements CacheStategy + * @param Key + * @param Value + */ +public class LRUCache implements CacheStrategy { + + private Map map; + + LRUCache(final Integer capacity){ + map = new LinkedHashMap(capacity, 0.75F, true) { + @Override + protected boolean removeEldestEntry(Map.Entry eldest){ + return size() > capacity; + } + }; + } + + @Override + public V getObject(K key) { + return map.get(key); + } + + @Override + public void putObject(K key, V value) { + map.put(key,value); + } +} diff --git a/appc-core/appc-common-bundle/java/org/onap/appc/cache/impl/MetadataCacheFactory.java b/appc-core/appc-common-bundle/java/org/onap/appc/cache/impl/MetadataCacheFactory.java new file mode 100644 index 000000000..b6398e8a7 --- /dev/null +++ b/appc-core/appc-common-bundle/java/org/onap/appc/cache/impl/MetadataCacheFactory.java @@ -0,0 +1,65 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * 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.appc.cache.impl; + +import org.onap.appc.cache.CacheStrategies; +import org.onap.appc.cache.MetadataCache; + +/** + * Metadata Cache Factory + */ +public class MetadataCacheFactory { + + private static class ReferenceHolder { + private ReferenceHolder() { + throw new IllegalAccessError("ReferenceHolder"); + } + + private static final MetadataCacheFactory FACTORY = new MetadataCacheFactory(); + } + + private MetadataCacheFactory() { + // do nothing + } + + public static MetadataCacheFactory getInstance(){ + return ReferenceHolder.FACTORY; + } + + public MetadataCache getMetadataCache(){ + return new MetadataCacheImpl(); + } + + /** + * Get MetadataCache + * @param cacheStrategy the CacheStrategies to be used to build MetadataCacheImpl + * @return a new instance of MetadataCacheImpl + */ + public MetadataCache getMetadataCache(CacheStrategies cacheStrategy) { + return new MetadataCacheImpl(cacheStrategy); + } + + + +} diff --git a/appc-core/appc-common-bundle/java/org/onap/appc/cache/impl/MetadataCacheImpl.java b/appc-core/appc-common-bundle/java/org/onap/appc/cache/impl/MetadataCacheImpl.java new file mode 100644 index 000000000..92b70c95d --- /dev/null +++ b/appc-core/appc-common-bundle/java/org/onap/appc/cache/impl/MetadataCacheImpl.java @@ -0,0 +1,68 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * 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.appc.cache.impl; + +import org.onap.appc.cache.CacheStrategies; +import org.onap.appc.cache.CacheStrategy; +import org.onap.appc.cache.MetadataCache; + +/** + * Implementation of MetadataCache + * @param Key + * @param Value + */ +public class MetadataCacheImpl implements MetadataCache { + + private CacheStrategy strategy; + + MetadataCacheImpl(){ + this(CacheStrategies.LRU); + } + + MetadataCacheImpl(CacheStrategies strategy){ + this.strategy = initializeStrategy(strategy); + } + + private CacheStrategy initializeStrategy(CacheStrategies strategy) { + if (strategy != null) { + switch (strategy) { + case LRU: + return new LRUCache<>(50); + default: + // do nothing + } + } + return null; + } + + @Override + public V getObject(K key) { + return (V)strategy.getObject(key); + } + + @Override + public void putObject(K key, V value) { + strategy.putObject(key, value); + } +} diff --git a/appc-core/appc-common-bundle/java/org/onap/appc/concurrent/Signal.java b/appc-core/appc-common-bundle/java/org/onap/appc/concurrent/Signal.java new file mode 100644 index 000000000..2daa0cf52 --- /dev/null +++ b/appc-core/appc-common-bundle/java/org/onap/appc/concurrent/Signal.java @@ -0,0 +1,230 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * 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.appc.concurrent; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeoutException; + +import org.onap.appc.util.StringHelper; + +/** + * This class is used to synchronize signaling of status between threads. + *

+ * In complex multi-threaded applications it is often necessary to synchronize operations between threads. This is + * especially true in complex algorithms where processing dependencies exist between different threads and the + * synchronization of the operations of those threads is required. This class is a framework to enable multi-thread + * signaling and wait/post logic that makes the thread synchronization easier. + *

+ *

+ * Basically, in thread synchronization, one thread is the "waiter" and one or more other threads are the "notifiers". + * The notifiers send signals to the waiter to inform that thread that certain conditions are true, processing has been + * completed, or to inform the waiter of the state of the other thread(s). In the basic java framework, the waiter and + * notifier are simply using the wait/notify mechanism provided, which does not allow for different conditions, state, + * or "signals" to exist. The wait/notify mechanism, in combination with the object mutex, provides basic blocking and + * releasing of a thread's dispatching state. + *

+ *

+ * This class builds upon the java wait/notify mechanism and allows for "signals" to be defined. These signals are + * simply string constants that mean something to the waiter and notifier threads. Any number of signals may be defined, + * and it is possible to wait for more than one signal to be received, wait for any one of a set to be received, or to + * test if a signal has been received without blocking. + *

+ *

+ * Some operations are blocking operations. These stop the execution of the calling thread until the specified condition + * is true. These blocking methods are all named "wait...", such as {@link #waitFor(String...)} and + * {@link #waitForAny(String...)}. The thread making the call to these blocking methods MUST be the waiter thread (the + * thread registered with the signal object). + *

+ *

+ * Some operations are non-blocking. These operations allow for the testing or setting of signal conditions and do not + * block the caller. When calling these methods ({@link #isSignaled(String)}, {@link #signal(String)}, and + * {@link #setTimeout(long)} the waiter thread mutex will be held and may block the waiter thread for the duration of + * the method call. + *

+ */ +public class Signal { + + /** + * The thread must be the thread of the waiter that is waiting for the signals to be received. It is the recipient + * of the signaled condition. This allows any number of other threads to send signals to the recipient and have the + * recipient synchronize its operation with the receipt of the appropriate signal(s). + */ + private Thread thread; + + /** + * The amount of time to wait for a signal to be receieved. Set to zero to wait forever. + */ + private long timeout = 0L; + + /** + * The collection of all received signals. Note, this need not be a synchronized collection because it will always + * be accessed while holding the mutex of the thread, therefore it is implicitly synchronized. + */ + private List receivedSignals; + + /** + * A signal object must access a thread that is waiting for the receipt of the signal(s). + */ + public Signal(Thread thread) { + this.thread = thread; + receivedSignals = new ArrayList(); + } + + /** + * Checks the waiter to see if it has been signaled + * + * @param signal + * The signal to check for + * @return True if the signal has been received, false otherwise + */ + public boolean isSignaled(String signal) { + synchronized (thread) { + return _signaled(signal); + } + } + + /** + * Sends the indicated signal to the waiter. + * + * @param signal + * The signal that is to be sent to the waiting thread and to notify it to process the signal. + */ + public void signal(String signal) { + synchronized (thread) { + if (!_signaled(signal)) { + receivedSignals.add(signal); + } + thread.notify(); + } + } + + /** + * Blocks the waiting thread until all of the indicated signals have been received, or the wait times out. + * + * @param signals + * The signals to be received. The waiter is blocked forever or until all of the signals are received. + * @throws TimeoutException + * If the wait has timed out waiting for a response + */ + public void waitFor(String... signals) throws TimeoutException { + long limit = System.currentTimeMillis() + timeout; + synchronized (thread) { + while (true) { + boolean complete = true; + for (String signal : signals) { + if (!_signaled(signal)) { + complete = false; + } + } + + if (complete) { + receivedSignals.removeAll(Arrays.asList(signals)); + return; + } + + if (timeout > 0) { + if (System.currentTimeMillis() > limit) { + throw new TimeoutException(String.format("Signals %s not received in the allotted timeout.", + StringHelper.asList(signals))); + } + } + + try { + thread.wait(timeout); + } catch (InterruptedException e) { + /* + * Interrupted exceptions are ignored + */ + } + } + } + } + + /** + * This method blocks the waiter until at least one of the indicated signals have been received. + * + * @param signals + * A list of signals, any one of which will satisfy the wait condition + * @return The signal that satisfied the wait + * @throws TimeoutException + * If none of the signals have been received within the allotted time + */ + public String waitForAny(String... signals) throws TimeoutException { + long limit = System.currentTimeMillis() + timeout; + synchronized (thread) { + while (true) { + for (String signal : signals) { + if (!_signaled(signal)) { + receivedSignals.remove(signal); + return signal; + } + } + + if (timeout > 0) { + if (System.currentTimeMillis() > limit) { + throw new TimeoutException( + String.format("One of signals \"%s\" not received in the allotted timeout.", + StringHelper.asList(signals))); + } + } + + try { + thread.wait(timeout); + } catch (InterruptedException e) { + /* + * Interrupted exceptions are ignored + */ + } + } + } + } + + /** + * This private method is used to handle the check for signaled status. Note that this method assumes the caller + * holds the thread mutex. + * + * @param signals + * The list of signals to check for + * @return True if any one of the signals has been received. + */ + private boolean _signaled(String... signals) { + for (String signal : signals) { + if (receivedSignals.contains(signal)) { + return true; + } + } + return false; + } + + /** + * Sets the timeout value for waiting for signals to be received + * + * @param timeout + */ + public void setTimeout(long timeout) { + this.timeout = timeout; + } +} diff --git a/appc-core/appc-common-bundle/java/org/onap/appc/configuration/Configuration.java b/appc-core/appc-common-bundle/java/org/onap/appc/configuration/Configuration.java new file mode 100644 index 000000000..0cc3f2699 --- /dev/null +++ b/appc-core/appc-common-bundle/java/org/onap/appc/configuration/Configuration.java @@ -0,0 +1,242 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * 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.appc.configuration; + +import java.util.Properties; + +import org.slf4j.Logger; + + + +/** + * This interface defines the common configuration support that is available to the application. + *

+ * Where properties are common to all CDP components (server, coordinator, and EPM), the property symbolic values are + * defined as part of this interface. Where they are unique to each component, they must be defined within that + * component. + *

+ */ +public interface Configuration { + + String PROPERTY_BOOTSTRAP_FILE_NAME = "org_onap_appc_bootstrap_file"; // + String DEFAULT_BOOTSTRAP_FILE_NAME = "appc.properties"; + String PROPERTY_BOOTSTRAP_FILE_PATH = "org_onap_appc_bootstrap_path"; // + String DEFAULT_BOOTSTRAP_FILE_PATH = "/opt/onap/appc/data/properties,${user.home},etc,../etc"; + String PROPERTY_RESOURCE_BUNDLES = "org.onap.appc.resources"; + String DEFAULT_RESOURCE_BUNDLES = "org/onap/appc/i18n/MessageResources"; + + /** + * This method is called to obtain a property expressed as a boolean value (true or false). The standard rules for + * {@link Boolean#valueOf(String)} are used. + * + * @param key + * The property key + * @return The value of the property expressed as a boolean, or false if it does not exist. + */ + boolean getBooleanProperty(String key); + + /** + * This method is called to obtain a property expressed as a boolean value (true or false). The standard rules for + * {@link Boolean#valueOf(String)} are used. + * + * @param key + * The property key + * @param defaultValue + * The default value to be returned if the property does not exist + * @return The value of the property expressed as a boolean, or false if it does not exist. + */ + boolean getBooleanProperty(String key, boolean defaultValue); + + /** + * Returns the indicated property value expressed as a floating point double-precision value (double). The standard + * rules for {@link Double#valueOf(String)} are used. + * + * @param key + * The property to retrieve + * @return The value of the property, or 0.0 if not found or invalid + */ + double getDoubleProperty(String key); + + /** + * Returns the indicated property value expressed as a floating point double-precision value (double). The standard + * rules for {@link Double#valueOf(String)} are used. + * + * @param key + * The property to retrieve + * @param defaultValue + * The default value to be returned if the property does not exist + * @return The value of the property, or 0.0 if not found or invalid + */ + double getDoubleProperty(String key, double defaultValue); + + /** + * Returns the property indicated expressed as an integer. The standard rules for + * {@link Integer#parseInt(String, int)} using a radix of 10 are used. + * + * @param key + * The property name to retrieve. + * @return The value of the property, or 0 if it does not exist or is invalid. + */ + int getIntegerProperty(String key); + + /** + * Returns the property indicated expressed as an integer. The standard rules for + * {@link Integer#parseInt(String, int)} using a radix of 10 are used. + * + * @param key + * The property name to retrieve. + * @param defaultValue + * The default value to be returned if the property does not exist + * @return The value of the property, or 0 if it does not exist or is invalid. + */ + int getIntegerProperty(String key, int defaultValue); + + /** + * Returns the specified property as a long integer value, if it exists, or zero if it does not. + * + * @param key + * The key of the property desired. + * @return The value of the property expressed as an integer long value, or zero if the property does not exist or + * is not a valid integer long. + */ + long getLongProperty(String key); + + /** + * Returns the specified property as a long integer value, if it exists, or the default value if it does not exist + * or is invalid. + * + * @param key + * The key of the property desired. + * @param defaultValue + * the value to be returned if the property is not valid or does not exist. + * @return The value of the property expressed as an integer long value, or the default value if the property does + * not exist or is not a valid integer long. + */ + long getLongProperty(String key, long defaultValue); + + /** + * This method can be called to retrieve a properties object that is immutable. Any attempt to modify the properties + * object returned will result in an exception. This allows a caller to view the current configuration as a set of + * properties. + * + * @return An unmodifiable properties object. + */ + Properties getProperties(); + + /** + * This method is called to obtain a property as a string value + * + * @param key + * The key of the property + * @return The string value, or null if it does not exist. + */ + String getProperty(String key); + + /** + * This method is called to obtain a property as a string value + * + * @param key + * The key of the property + * @param defaultValue + * The default value to be returned if the property does not exist + * @return The string value, or null if it does not exist. + */ + String getProperty(String key, String defaultValue); + + /** + * Returns true if the named property is defined, false otherwise. + * + * @param key + * The key of the property we are interested in + * @return True if the property exists. + */ + boolean isPropertyDefined(String key); + + /** + * Returns an indication of the validity of the boolean property. A boolean property is considered to be valid only + * if it has the value "true" or "false" (ignoring case). + * + * @param key + * The property to be checked + * @return True if the value is a boolean constant, or false if it does not exist or is not a correct string + */ + boolean isValidBoolean(String key); + + /** + * Returns an indication if the indicated property represents a valid double-precision floating point number. + * + * @param key + * The property to be examined + * @return True if the property is a valid representation of a double, or false if it does not exist or contains + * illegal characters. + */ + boolean isValidDouble(String key); + + /** + * Returns an indication if the property is a valid integer value or not. + * + * @param key + * The key of the property to check + * @return True if the value is a valid integer string, or false if it does not exist or contains illegal + * characters. + */ + boolean isValidInteger(String key); + + /** + * Determines is the specified property exists and is a valid representation of an integer long value. + * + * @param key + * The property to be checked + * @return True if the property is a valid representation of an integer long value, and false if it either does not + * exist or is not valid. + */ + boolean isValidLong(String key); + + /** + * This method allows the caller to set all properties from a provided properties object into the configuration + * property set. + *

+ * The primary difference between this method and the factory method + * {@link ConfigurationFactory#getConfiguration(Properties)} is that this method does not clear and reload the + * configuration. Rather, this method merges the provided properties object contents into the existing properties, + * replacing any same-named keys with the values from this object. + *

+ * + * @param properties + * The properties object to copy all properties from + */ + void setProperties(Properties properties); + + /** + * This method allows a caller to insert a new property definition into the configuration object. This allows the + * application to adjust or add to the current configuration. If the property already exists, it is replaced with + * the new value. + * + * @param key + * The key of the property to be defined + * @param value + * The value of the property to be defined + */ + void setProperty(String key, String value); +} diff --git a/appc-core/appc-common-bundle/java/org/onap/appc/configuration/ConfigurationFactory.java b/appc-core/appc-common-bundle/java/org/onap/appc/configuration/ConfigurationFactory.java new file mode 100644 index 000000000..9ea5083fe --- /dev/null +++ b/appc-core/appc-common-bundle/java/org/onap/appc/configuration/ConfigurationFactory.java @@ -0,0 +1,433 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * 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.appc.configuration; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.text.DateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.Optional; +import java.util.Properties; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; +import org.onap.appc.i18n.Msg; +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import com.att.eelf.i18n.EELFResourceManager; + +/** + * The configuration factory is used to obtain access to an already created and initialized + * singleton configuration object as well as to create and initialize the singleton if not already + * set up. + *

+ * This class is responsible for the creation of the configuration object used to manage the + * configuration of the application. The configuration object implementation must implement the + * Configuration interface. This allows for the factory to create different + * specializations in the future if needed and not break any application code. + *

+ *

+ * The configuration object is basically a wrapper around a properties object. The configuration is + * therefore specified as a set of properties that are loaded and processed from different sources + * with different precedences. It is important that the configuration object always be able to + * supply default values for any configuration properties that must be supplied, and not rely on the + * user always supplying these values. This also relieves the application itself from having to + * interpret missing or invalid properties and applying defaults. By having all of the defaults in + * one place, the application code can be simpler (not having to worry about defaults or invalid + * properties), and the defaults can be changed much easier (they are all in one place and not + * distributed throughout the codebase). + *

+ *

+ * Since the configuration is managed as a property object, we can use a characteristic of the + * Properties class to our advantage. Namely, if we put a property into a + * Properties object that already exists, the Properties object replaces + * it with the new value. This does not affect any other properties that may already be defined in + * the properties object. This gives us the ability to initialize the properties with default values + * for all of the application settings, then override just those that we need to override, possibly + * from multiple sources and in increasing order of precedence. + *

+ *

+ * This means that properties are in effect "merged" together from multiple sources in a prescribed + * precedence order. In fact, the precedence order that this factory implements is defined as: + *

+ *
    + *
  1. Default values from a system resource file.
  2. + *
  3. User-supplied properties file, if any.
  4. + *
  5. Application-supplied properties, if any.
  6. + *
  7. Command-line properties (if any)
  8. + *
+ *

+ * The name and location of the properties file that is loaded can also be set, either in the + * defaults, overridden by the system command line via -D, or as a system environment variable. + * There are two properties that can be specified to define the name and path. These are: + *

+ *
+ *
org.onap.appc.bootstrap.file
+ *
This property defines the name of the file that will be loaded. If not specified, the default + * value is "appc.properties". This can be specified in either (or both) the default properties or + * the command line. The command line specification will always override.
+ *
org.onap.appc.bootstrap.path
+ *
This is a comma-delimited (,) path of directories to be searched to locate the specified + * file. The first occurrence of the file is the one loaded, and no additional searching is + * performed. The path can be specified in either, or both, the default values and the command line + * specification. If specified on the command line, the value overrides the default values. If + * omitted, the default path is $/opt/onap/appc/data/properties,${user.home},.
+ *
+ * + * @since Mar 18, 2014 + * @version $Id$ + */ +public final class ConfigurationFactory { + + private static final EELFLogger logger = EELFManager.getInstance().getLogger(ConfigurationFactory.class); + + /** + * This is a string constant for the comma character. It's intended to be used a common string + * delimiter. + */ + private static final String COMMA = ","; + + /** + * The default Configuration object that implements the Configuration interface and + * represents our system configuration settings. + */ + private static DefaultConfiguration config = null; + + /** + * The default properties resource to be loaded + */ + private static final String DEFAULT_PROPERTIES = "org/onap/appc/default.properties"; + + /** + * This collection allows for special configurations to be created and maintained, organized by + * some identification (such as an object reference to the StackBuilder to which they apply), + * and then obtained from the configuration factory when needed. + */ + private static HashMap localConfigs = new HashMap<>(); + + /** + * The reentrant shared lock used to serialize access to the properties. + */ + private static ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + + /** + * This is a constant array of special property names that will be copied from the configuration + * back to the System properties object if they are defined in the configuration AND they do not + * already exist in the System properties object. These are intended as a convenience for + * setting the AFT properties for the Discovery client where it may be difficult or impossible + * to set VM arguments for the container. + */ + private static final String[] specialProperties = + {"AFT_LATITUDE", "AFT_LONGITUDE", "AFT_ENVIRONMENT", "SCLD_PLATFORM"}; + + private ConfigurationFactory() {} + + /** + * This method is used to obtain the common configuration object (as well as set it up if not + * already). + * + * @return The configuration object implementation + */ + public static Configuration getConfiguration() { + + /* + * First, attempt to access the properties as a read lock holder + */ + ReadLock readLock = lock.readLock(); + readLock.lock(); + try { + + /* + * If the properties don't exist, release the read lock and acquire the write lock. Once + * we get the write lock, we need to re-check to see that the configuration needs to be + * set up (because another thread may have beat us to it). After we get a configuration + * set up, release the write lock and re-obtain the read lock to access the properties. + */ + if (config == null) { + readLock.unlock(); + WriteLock writeLock = lock.writeLock(); + writeLock.lock(); + try { + if (config == null) { + config = new DefaultConfiguration(); + initialize(null); + } + } catch (Exception e){ + logger.error("getConfiguration", e); + } finally { + writeLock.unlock(); + } + readLock.lock(); + } + } finally { + readLock.unlock(); + } + return config; + } + + /** + * This method will obtain the local configuration for the specified object if it exists, or + * will create it from the current global configuration. This allows the configuration to be + * tailored for a specific process or operation, and uniquely identified by some value (such as + * the object that represents the special use of the configuration). + * + * @param owner The owner or identification of the owner of the special configuration + * @return The special configuration object, or a clone of the global configuration so that it + * can be altered if needed. + */ + public static Configuration getConfiguration(final Object owner) { + DefaultConfiguration local; + ReadLock readLock = lock.readLock(); + readLock.lock(); + try { + local = (DefaultConfiguration) localConfigs.get(owner); + if (local == null) { + readLock.unlock(); + WriteLock writeLock = lock.writeLock(); + writeLock.lock(); + local = (DefaultConfiguration) localConfigs.get(owner); + if (local == null) { + local = getClonedDefaultConfiguration(owner, local); + } + writeLock.unlock(); + } + readLock.lock(); + } finally { + readLock.unlock(); + } + return local; + } + + /** + * This method allows the caller to alter the configuration, supplying the specified + * configuration properties which override the application default values. + *

+ * The configuration is re-constructed (if already constructed) or created new (if not already + * created) and the default properties are loaded into the configuration. + *

+ *

+ * The primary purpose of this method is to allow the application configuration properties to be + * reset or refreshed after the application has already been initialized. This method will lock + * the configuration for the duration while it is being re-built, and should not be called on a + * regular basis. + *

+ * + * @param props The properties used to configure the application. + * @return Access to the configuration implementation + */ + public static Configuration getConfiguration(final Properties props) { + WriteLock writeLock = lock.writeLock(); + writeLock.lock(); + try { + config = new DefaultConfiguration(); + initialize(props); + return config; + } finally { + writeLock.unlock(); + } + } + + private static DefaultConfiguration getClonedDefaultConfiguration(Object owner, DefaultConfiguration local) { + Optional global = + Optional.ofNullable((DefaultConfiguration) getConfiguration()); + try { + if (global.isPresent()) { + local = (DefaultConfiguration) global.get().clone(); + } + } catch (CloneNotSupportedException e) { + logger.error("getClonedDefaultConfiguration", e); + } + localConfigs.put(owner, local); + return local; + } + + /** + * This method will clear the current configuration and then re-initialize it with the default + * values, application-specific configuration file, user-supplied properties (if any), and then + * command-line settings. + *

+ * This method MUST be called holding the configuration lock! + *

+ *

+ * This method is a little special in that logging messages generated during the method must be + * cached and delayed until after the logging framework has been initialized. After that, the + * delayed logging buffer can be dumped to the log file and cleared. + *

+ * + * @param props Application-supplied configuration values, if any + */ + private static void initialize(final Properties props) { + DateFormat format = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG); + Date now = new Date(); + logger.info( + "------------------------------------------------------------------------------"); + + logger.info(Msg.CONFIGURATION_STARTED, format.format(now)); + + /* + * Clear any existing properties + */ + config.clear(); + logger.info(Msg.CONFIGURATION_CLEARED); + + /* + * Load the defaults (if any are present) + */ + InputStream in = Thread.currentThread().getContextClassLoader() + .getResourceAsStream(DEFAULT_PROPERTIES); + if (in != null) { + logger.info(Msg.LOADING_DEFAULTS, DEFAULT_PROPERTIES); + try { + config.setProperties(in); + } finally { + try { + in.close(); + } catch (IOException e) { + logger.error("Cannot close inputStream", e); + } + } + for (String key : config.getProperties().stringPropertyNames()) { + logger.info(Msg.PROPERTY_VALUE, key, config.getProperty(key)); + } + } else { + logger.info(Msg.NO_DEFAULTS_FOUND, DEFAULT_PROPERTIES); + } + + /* + * Look for application configuration property file. By default, we will look for the file + * "cdp.properties" on the user home path, then on "./etc" (relative to current path), then + * on "../etc" (relative to current path). If we do not find any property file, then we + * continue. Otherwise, we load the first property file we find and then continue. In order + * to allow default values for the filename and paths to be searched, we first attempt to + * obtain these from our configuration object (which should be primed with default values + * and/or overridden with application-specified values). We then use the values obtained + * from that to get any user supplied values on the command line. + */ + String filename = config.getProperty(Configuration.PROPERTY_BOOTSTRAP_FILE_NAME, + Configuration.DEFAULT_BOOTSTRAP_FILE_NAME); + filename = System.getProperty(Configuration.PROPERTY_BOOTSTRAP_FILE_NAME, filename); + String env = System.getenv(Configuration.PROPERTY_BOOTSTRAP_FILE_NAME); + if (env != null && env.trim().length() > 0) { + filename = env; + } + + String path = config.getProperty(Configuration.PROPERTY_BOOTSTRAP_FILE_PATH, + Configuration.DEFAULT_BOOTSTRAP_FILE_PATH); + path = System.getProperty(Configuration.PROPERTY_BOOTSTRAP_FILE_PATH, path); + env = System.getenv(Configuration.PROPERTY_BOOTSTRAP_FILE_PATH); + if (env != null && env.trim().length() > 0) { + path = env; + } + + logger.info(Msg.SEARCHING_CONFIGURATION_OVERRIDES, path, filename); + + String[] pathElements = path.split(COMMA); + boolean found = false; + for (String pathElement : pathElements) { + File file = new File(pathElement, filename); + if (file.exists() && file.canRead() && !file.isDirectory()) { + + logger.info(Msg.LOADING_CONFIGURATION_OVERRIDES, file.getAbsolutePath()); + Properties fileProperties = new Properties(); + BufferedInputStream stream = null; + try { + stream = new BufferedInputStream(new FileInputStream(file)); + fileProperties.load(stream); + for (String key : fileProperties.stringPropertyNames()) { + logger.debug(Msg.PROPERTY_VALUE, key, fileProperties.getProperty(key)); + config.setProperty(key, fileProperties.getProperty(key)); + } + found = true; + break; + } catch (IOException e) { + logger.error(EELFResourceManager.format(e)); + } finally { + try { + if (stream != null) { + stream.close(); + } + } catch (IOException e) { + logger.error("Unable to close stream", e); + } + } + } + } + + if (!found) { + logger.warn(Msg.NO_OVERRIDE_PROPERTY_FILE_LOADED, filename, path); + } + + /* + * Apply any application-specified properties + */ + if (props != null) { + logger.info(Msg.LOADING_APPLICATION_OVERRIDES); + for (String key : props.stringPropertyNames()) { + logger.debug(Msg.PROPERTY_VALUE, key, props.getProperty(key)); + config.setProperty(key, props.getProperty(key)); + } + } else { + logger.info(Msg.NO_APPLICATION_OVERRIDES); + } + + /* + * Merge in the System.properties to pick-up any command line arguments (-Dkeyword=value) + */ + logger.info(Msg.MERGING_SYSTEM_PROPERTIES); + config.setProperties(System.getProperties()); + + /* + * As a convenience, copy the "specialProperties" that are not defined in System.properties + * from the configuration back to the system properties object. + */ + for (String key : config.getProperties().stringPropertyNames()) { + for (String specialProperty : specialProperties) { + if (key.equals(specialProperty) && !System.getProperties().containsKey(key)) { + System.setProperty(key, config.getProperty(key)); + logger.info(Msg.SETTING_SPECIAL_PROPERTY, key, config.getProperty(key)); + } + } + } + + /* + * Initialize the resource manager by loading the requested bundles, if any are defined. + * Resource bundles may be specified as a comma-delimited list of names. These resource + * names are base names of resource bundles, do not include the language or country code, or + * the ".properties" extension. The actual loading of the resource bundles is done lazily + * when requested the first time. If the bundle does not exist, or cannot be loaded, it is + * ignored. + */ + String resourcesList = config.getProperty(Configuration.PROPERTY_RESOURCE_BUNDLES, + Configuration.DEFAULT_RESOURCE_BUNDLES); + String[] resources = resourcesList.split(","); + for (String resource : resources) { + logger.info(Msg.LOADING_RESOURCE_BUNDLE, resource.trim()); + EELFResourceManager.loadMessageBundle(resource.trim()); + } + } +} diff --git a/appc-core/appc-common-bundle/java/org/onap/appc/configuration/DefaultConfiguration.java b/appc-core/appc-common-bundle/java/org/onap/appc/configuration/DefaultConfiguration.java new file mode 100644 index 000000000..635110cf0 --- /dev/null +++ b/appc-core/appc-common-bundle/java/org/onap/appc/configuration/DefaultConfiguration.java @@ -0,0 +1,564 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * 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.appc.configuration; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.security.CodeSource; +import java.security.ProtectionDomain; +import java.security.Provider; +import java.security.Provider.Service; +import java.security.Security; +import java.util.Map.Entry; +import java.util.Properties; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.onap.appc.encryption.EncryptionTool; +import org.onap.appc.util.UnmodifiableProperties; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class provides the implementation of the Configuration interface. It is created + * by the ConfigurationFactory and initialized with the configuration values for the process. + * + * @since Mar 18, 2014 + */ +public final class DefaultConfiguration implements Configuration, Cloneable { + + private static final Logger logger = LoggerFactory.getLogger(DefaultConfiguration.class); + + /** + * The framework configuration properties. + */ + private Properties properties = new Properties(); + + /** + * Construct the configuration object. + */ + DefaultConfiguration() {} + + /** + * Clears all properties + */ + public void clear() { + properties.clear(); + } + + /** + * @see java.lang.Object#clone() + */ + @Override + protected Object clone() throws CloneNotSupportedException { + DefaultConfiguration clone = (DefaultConfiguration) super.clone(); + + clone.properties = new Properties(this.properties); + clone.properties.putAll(this.properties); + + return clone; + } + + /** + * Decrypts an encrypted value, if it is encrypted, and returns the clear text. Performs no + * operation on the string if it is not encrypted. + * + * @param value The value to (optionally) be decrypted + * @return The clear text + */ + @SuppressWarnings("nls") + private static String decrypt(String value) { + if (value != null && value.startsWith(EncryptionTool.ENCRYPTED_VALUE_PREFIX)) { + try { + return EncryptionTool.getInstance().decrypt(value); + } catch (Exception e) { + StringBuilder out = new StringBuilder(); + for (Provider p : Security.getProviders()) { + for (Service s : p.getServices()) { + String algo = s.getAlgorithm(); + out.append(String.format( + "\n==Found Algorithm [ %s ] in provider [ %s ] and service [ %s ]", + algo, p.getName(), s.getClassName())); + } + } + logger.debug(out.toString()); + logger.warn(String.format("Could not decrypt the configuration value [%s]", value), + e); + } + } + return value; + } + + /** + * Decrypts all elements in the properties object + */ + private void decryptAllProperties() { + if (properties != null) { + for (Entry e : properties.entrySet()) { + if (e.getValue() != null) { + e.setValue(decrypt(e.getValue().toString())); + } + } + } + } + + /** + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + + if (obj == null) { + return false; + } + + if (this.getClass() != obj.getClass()) { + return false; + } + + DefaultConfiguration other = (DefaultConfiguration) obj; + + return (this.properties.size() == other.properties.size()) + && (this.properties.entrySet().containsAll(other.properties.entrySet())) + && (other.properties.entrySet().containsAll(this.properties.entrySet())); + + } + + /** + * This method will use the properties object to expand any variables that may be present in the + * template provided. Variables are represented by the string "${name}", where "name" is the + * name of a property defined in either the current configuration object, or system properties + * if undefined. If the value cannot be found, the variable is removed and an empty string is + * used to replace the variable. + * + * @param template The template to be expanded + * @return The expanded template where each variable is replaced with its value + */ + @SuppressWarnings("nls") + private String expandVariables(String template) { + if (template == null) { + return null; + } + + // Decrypt the template if needed + // template = decrypt(template); DH: Do not assign values to parameters, bad form! Also, + // Sonar complains + // bitterly + + StringBuilder builder = new StringBuilder(decrypt(template)); + Pattern pattern = Pattern.compile("\\$\\{([^\\}]+)\\}"); + Matcher matcher = pattern.matcher(builder); + while (matcher.find()) { + String variable = matcher.group(1); + String value = properties.getProperty(variable); + if (value == null) { + value = System.getProperty(variable); + } + if (value == null) { + value = ""; + } + builder.replace(matcher.start(), matcher.end(), value); + + matcher.reset(); + } + return builder.toString().trim(); + } + + /** + * This method is called to obtain a property expressed as a boolean value (true or false). The + * standard rules for Boolean.parseBoolean() are used. + * + * @param key The property key + * @return The value of the property expressed as a boolean, or false if it does not exist. + */ + @SuppressWarnings("nls") + @Override + public boolean getBooleanProperty(String key) { + return Boolean.valueOf(getProperty(key, "false")); + } + + /** + * This method is called to obtain a property expressed as a boolean value (true or false). The + * standard rules for Boolean.valueOf(String) are used. + * + * @param key The property key + * @param defaultValue The default value to be returned if the property does not exist + * @return The value of the property expressed as a boolean, or false if it does not exist. + * @see org.onap.appc.configuration.Configuration#getBooleanProperty(java.lang.String, boolean) + */ + @Override + public boolean getBooleanProperty(String key, boolean defaultValue) { + if (isPropertyDefined(key)) { + return getBooleanProperty(key); + } + return defaultValue; + } + + /** + * Returns the indicated property value expressed as a floating point double-precision value + * (double). + * + * @param key The property to retrieve + * @return The value of the property, or 0.0 if not found + * @see org.onap.appc.configuration.Configuration#getDoubleProperty(java.lang.String) + */ + @SuppressWarnings("nls") + @Override + public double getDoubleProperty(String key) { + try { + return Double.valueOf(getProperty(key, "0.0")); + } catch (NumberFormatException e) { + return 0.0; + } + } + + /** + * This method is called to obtain a property as a string value + * + * @param key The key of the property + * @param defaultValue The default value to be returned if the property does not exist + * @return The string value, or null if it does not exist. + * @see org.onap.appc.configuration.Configuration#getDoubleProperty(java.lang.String, double) + */ + @Override + public double getDoubleProperty(String key, double defaultValue) { + if (isPropertyDefined(key)) { + return getDoubleProperty(key); + } + return defaultValue; + } + + /** + * Returns the property indicated expressed as an integer. The standard rules for + * {@link Integer#parseInt(String, int)} using a radix of 10 are used. + * + * @param key The property name to retrieve. + * @return The value of the property, or 0 if it does not exist or is invalid. + * @see org.onap.appc.configuration.Configuration#getIntegerProperty(java.lang.String) + */ + @SuppressWarnings("nls") + @Override + public int getIntegerProperty(String key) { + try { + return Integer.parseInt(getProperty(key, "0"), 10); + } catch (NumberFormatException e) { + return 0; + } + } + + /** + * Returns the property indicated expressed as an integer. The standard rules for + * Integer.parseInt(String, int) using a radix of 10 are used. + * + * @param key The property name to retrieve. + * @param defaultValue The default value to be returned if the property does not exist + * @return The value of the property, or 0 if it does not exist or is invalid. + * @see org.onap.appc.configuration.Configuration#getIntegerProperty(java.lang.String, int) + */ + @Override + public int getIntegerProperty(String key, int defaultValue) { + if (isPropertyDefined(key)) { + return getIntegerProperty(key); + } + return defaultValue; + } + + /** + * Returns the specified property as a long integer value, if it exists, or zero if it does not. + * + * @param key The key of the property desired. + * @return The value of the property expressed as an integer long value, or zero if the property + * does not exist or is not a valid integer long. + * @see org.onap.appc.configuration.Configuration#getLongProperty(java.lang.String) + */ + @SuppressWarnings("nls") + @Override + public long getLongProperty(String key) { + try { + return Long.parseLong(getProperty(key, "0"), 10); + } catch (NumberFormatException e) { + return 0; + } + } + + /** + * Returns the specified property as a long integer value, if it exists, or the default value if + * it does not exist or is invalid. + * + * @param key The key of the property desired. + * @param defaultValue the value to be returned if the property is not valid or does not exist. + * @return The value of the property expressed as an integer long value, or the default value if + * the property does not exist or is not a valid integer long. + * @see org.onap.appc.configuration.Configuration#getLongProperty(java.lang.String, long) + */ + @Override + public long getLongProperty(String key, long defaultValue) { + if (isPropertyDefined(key)) { + return getLongProperty(key); + } + return defaultValue; + } + + /** + * This method can be called to retrieve a properties object that is immutable. Any attempt to + * modify the properties object returned will result in an exception. This allows a caller to + * view the current configuration as a set of properties. + * + * @return An unmodifiable properties object. + * @see org.onap.appc.configuration.Configuration#getProperties() + */ + @Override + public Properties getProperties() { + return new UnmodifiableProperties(properties); + } + + /** + * This method is called to obtain a property as a string value + * + * @param key The key of the property + * @return The string value, or null if it does not exist. + */ + @Override + public String getProperty(String key) { + String value = properties.getProperty(key); + if (value == null) { + return null; + } + return expandVariables(value.trim()); + } + + /** + * This method is called to obtain a property as a string value + * + * @param key The key of the property + * @param defaultValue The default value to be returned if the property does not exist + * @return The string value, or null if it does not exist. + * @see org.onap.appc.configuration.Configuration#getProperty(java.lang.String, + * java.lang.String) + */ + @Override + public String getProperty(String key, String defaultValue) { + if (isPropertyDefined(key)) { + return getProperty(key); + } + + if (defaultValue == null) { + return null; + } + + return expandVariables(defaultValue.trim()); + } + + /** + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return properties == null ? 0 : properties.hashCode(); + } + + /** + * Returns true if the named property is defined, false otherwise. + * + * @param key The key of the property we are interested in + * @return True if the property exists. + */ + @Override + public boolean isPropertyDefined(String key) { + return properties.containsKey(key); + } + + /** + * Returns an indication of the validity of the boolean property. A boolean property is + * considered to be valid only if it has the value "true" or "false" (ignoring case). + * + * @param key The property to be checked + * @return True if the value is a boolean constant, or false if it does not exist or is not a + * correct string + * @see org.onap.appc.configuration.Configuration#isValidBoolean(java.lang.String) + */ + @SuppressWarnings("nls") + @Override + public boolean isValidBoolean(String key) { + String value = getProperty(key); + if (value != null) { + value = value.toLowerCase(); + return value.matches("true|false"); + } + return false; + } + + /** + * Returns an indication if the indicated property represents a valid double-precision floating + * point number. + * + * @param key The property to be examined + * @return True if the property is a valid representation of a double, or false if it does not + * exist or contains illegal characters. + * @see org.onap.appc.configuration.Configuration#isValidDouble(java.lang.String) + */ + @Override + public boolean isValidDouble(String key) { + String value = getProperty(key); + if (value != null) { + try { + Double.valueOf(value); + return true; + } catch (NumberFormatException e) { + return false; + } + } + return false; + } + + /** + * Returns an indication if the property is a valid integer value or not. + * + * @param key The key of the property to check + * @return True if the value is a valid integer string, or false if it does not exist or + * contains illegal characters. + * @see org.onap.appc.configuration.Configuration#isValidInteger(java.lang.String) + */ + @Override + public boolean isValidInteger(String key) { + String value = getProperty(key); + if (value != null) { + try { + Integer.parseInt(value.trim(), 10); + return true; + } catch (NumberFormatException e) { + return false; + } + } + return false; + } + + /** + * Determines is the specified property exists and is a valid representation of an integer long + * value. + * + * @param key The property to be checked + * @return True if the property is a valid representation of an integer long value, and false if + * it either does not exist or is not valid. + * @see org.onap.appc.configuration.Configuration#isValidLong(java.lang.String) + */ + @Override + public boolean isValidLong(String key) { + String value = getProperty(key); + if (value != null) { + try { + Long.parseLong(value.trim(), 10); + return true; + } catch (NumberFormatException e) { + return false; + } + } + return false; + } + + /** + * This method allows an implementation to load configuration properties that may override + * default values. + * + * @param is An input stream that contains the properties to be loaded + */ + public void setProperties(InputStream is) { + try { + properties.load(is); + } catch (IOException e) { + logger.warn("setProperties with inputStream got exception", e); + } + } + + /** + * This method allows an implementation to load configuration properties that may override + * default values. + * + * @param props An optional Properties object to be merged into the configuration, replacing any + * same-named properties. + * @see org.onap.appc.configuration.Configuration#setProperties(java.util.Properties) + */ + @Override + public void setProperties(Properties props) { + properties.putAll(props); + decryptAllProperties(); + } + + /** + * This method allows a caller to insert a new property definition into the configuration + * object. This allows the application to adjust or add to the current configuration. If the + * property already exists, it is replaced with the new value. + * + * @param key The key of the property to be defined + * @param value The value of the property to be defined + * @see org.onap.appc.configuration.Configuration#setProperty(java.lang.String, + * java.lang.String) + */ + @Override + public void setProperty(String key, String value) { + properties.setProperty(key, decrypt(value)); + } + + /** + * @see java.lang.Object#toString() + */ + @SuppressWarnings("nls") + @Override + public String toString() { + return String.format("Configuration: %d properties, keys:[%s]", properties.size(), + properties.keySet().toString()); + } + + /** + * This is a helper method to read the manifest of the jar file that this class was loaded from. + * Note that this will only work if the code is packaged in a jar file. If it is an open + * deployment, such as under eclipse, this will not work and there is code added to detect that + * case. + * + * @return The manifest object from the jar file, or null if the code is not packaged in a jar + * file. + */ + @SuppressWarnings({"unused", "nls"}) + private Manifest getManifest() { + ProtectionDomain domain = getClass().getProtectionDomain(); + CodeSource source = domain.getCodeSource(); + URL location = source.getLocation(); + String path = location.getPath(); + int index = path.indexOf('!'); + if (index != -1) { + path = path.substring(0, index); + } + if (path.endsWith(".jar")) { + try (JarFile jar = new JarFile(location.getFile())) { + return jar.getManifest(); + } catch (IOException e) { + logger.error("getManifest", e); + } + } + + return null; + } +} diff --git a/appc-core/appc-common-bundle/java/org/onap/appc/configuration/package.html b/appc-core/appc-common-bundle/java/org/onap/appc/configuration/package.html new file mode 100644 index 000000000..c6889da9a --- /dev/null +++ b/appc-core/appc-common-bundle/java/org/onap/appc/configuration/package.html @@ -0,0 +1,174 @@ + + + + +Configuration + + + +

+ CDP Configuration support is provided by a common framework to load + and manage configuration properties from multiple sources. The basic + concept is to load a set of default properties from a known resource + file located on the class path, and then override these defaults + with optional settings that can be provided by a user through either + additional property files or via the command line (as VM arguments). + The loading of defaults from a resource property file (named com/att/cdp/default.properties) + ensures that values are defined for properties the application needs + in order to operate. +

+

+ One of these default values that can be set is the name of the + property file that allows the user to supply settings, as well as + the path where the file can be found. In general, the default name + of the property file will be "cdp.properties", + and the path that will be searched is "${user.home};etc;../etc". + However, these values can be changed through the use of the + default.properties resource file. The property that specifies the + property file name is named com.att.cdp.bootstrap.file, + while the property named com.att.cdp.bootstrap.path + specifies the search path. +

+

+ After the default.properties are loaded, but prior to searching for + the application configuration file, the configuration factory checks + for properties com.att.cdp.bootstrap.path and com.att.cdp.bootstrap.file + in the System properties object (meaning they were set by the + command line). If these values are defined in the system properties + object, they are used. If not, these values are obtained from the + default properties just loaded. This allows the specification of + either the file name or path, or both, to be overridden during start + up by using command-line arguments. +

+

The search path is scanned for the + first occurrence of the specified property file. The first + occurrence is loaded and scanning is stopped at that point. The + configuration factory does not load all occurrences it finds, only + the first occurrence it finds.

+

The configuration properties are + loaded and processed according to a defined precedence order, such + that properties defined with a higher precedence override the same + property at a lower precedence. The precedence order is defined as + follows:

+

Precedence Order

+
    +
  1. Default properties are initially loaded into the + configuration. These default properties are the lowest level + precedence, and will be overridden by any properties specified at + higher levels. These are loaded from resources that are packaged + as part of the various application components. Each component + (Server, Coordinator, EPM, or CLI) may have different default + properties. The default properties are loaded from a resource + named com/att/cdp/default.properties. The default + properties can specify the name of the application property file + to be used to configure the application, as well as the path to + search. Additionally, these properties can be supplied via the + command line to override the default settings if needed.

    +
  2. +
  3. The configuration factory allows for the application to + supply an initial properties object to initialize the + configuration. This properties object could be loaded or created + in any way necessary for the application. This is totally up to + the application to define, if it is needed. If no + application-specific property object is supplied, this step is + skipped. If a property object is supplied, it is used to replace + or set any properties that may have been defined by the defaults.
    +
    +
  4. +
  5. The configuration factory will then search for a bootstrap + file on a bootstrap path. The actual bootstrap file name and path + can be specified as properties on the command line, or will + default to a file name of cdp.properties and a + path of ${user.home};etc;../etc. If desired, the + user can specify the exact name of the property file to be loaded + as well as the path using -Dcom.att.cdp.bootstrap.file=<filename> + and -Dcom.att.cdp.bootstrap.path=<path>. + These properties are set to default values by the default + properties loaded in step #1 above. The first occurrence of a + property file is the file loaded and used. Any other occurrences + are not processed.

    +
  6. +
  7. The System properties are then merged into the + configuration. This allows the highest level of precedence, + command-line VM arguments (-Dname=value) to be + merged into the configuration property object. These settings + override all lower level settings of the same name, as well as + merge all system properties into the configuration object. +
  8. +
+

Variables

+

+ The configuration support allows for variables to be inserted into + any property that is defined. Variables are named using the format ${name}, + where the "name" is the name of a property that is defined + in the configuration, or a system property (such as user.home). + Variables can nest, such that a variable can be replaced with + another variable, which is then reevaluated to obtain the value. + This allows for indirection as well as variable substitution, if + needed. +

+

Using the Configuration Support

+

+ The configuration support was designed to be easy to use. The + configuration implementation is abstracted away from the application + so that it could be changed easily in the future if needed, or if we + needed to load different implementations for different reasons. This + means that the application always accesses the configuration through + an interface, named Configuration. The + implementation of that configuration interface is obtained by a + static method on the ConfigurationFactory class. + The configuration factory will both create the configuration if not + already created on the first access, as well as return the current + configuration if already created. Additionally, the + ConfigurationFactory provides mechanisms to recreate the + configuration after the application is initialized should the need + arise to update its configuration. +

+

An example of the code needed to + obtain access to the configuration is:

+
Configuration config = ConfigurationFactory.getConfiguration();
+

Please refer to the javadoc or the + source code in cdp-common for other ways that the configuration and + configuration factory can be used.

+

Reloading Properties

+

The configuration support allows + for properties to be re-loaded and re-evaluated after the + application is running. This is designed to allow a configuration to + be refreshed should the need arise. This could allow on-demand + refresh (via a command, for example), or automatically based on + sensing a change in the configuration file.

+

+ When the ConfigurationFactory method getConfiguration(Properties) + is called, the current configuration is cleared and rebuilt using + the process defined above. The supplied property object is used in + step #2 of the process. While the properties are being re-built, no + access to the properties are allowed. Any attempt to access + properties while the re-build operation is in effect will block the + caller until completed. This is accomplished using read and write + shared locks. +

+ + +>>>>>>> app-controller/master:appc-common/src/main/java/org/onap/appc/configuration/package.html diff --git a/appc-core/appc-common-bundle/java/org/onap/appc/encryption/EncryptionException.java b/appc-core/appc-common-bundle/java/org/onap/appc/encryption/EncryptionException.java new file mode 100644 index 000000000..804fe6a56 --- /dev/null +++ b/appc-core/appc-common-bundle/java/org/onap/appc/encryption/EncryptionException.java @@ -0,0 +1,31 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * 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.appc.encryption; + +public class EncryptionException extends Exception { + + public EncryptionException(String message) { + super(message); + } +} diff --git a/appc-core/appc-common-bundle/java/org/onap/appc/encryption/EncryptionTool.java b/appc-core/appc-common-bundle/java/org/onap/appc/encryption/EncryptionTool.java new file mode 100644 index 000000000..480b15e06 --- /dev/null +++ b/appc-core/appc-common-bundle/java/org/onap/appc/encryption/EncryptionTool.java @@ -0,0 +1,214 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * 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.appc.encryption; + +import java.security.Provider; +import java.security.Provider.Service; +import java.security.Security; + +import javax.crypto.Cipher; + +import org.jasypt.contrib.org.apache.commons.codec_1_3.binary.Base64; +import org.jasypt.util.text.BasicTextEncryptor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class is used to encapsulate the encryption and decryption support in one place and to provide a utility to + * encrypt and decrypt data. + */ +public class EncryptionTool { + + /** + * This lock object is used ONLY if the singleton has not been set up. + */ + private static final Object lock = new Object(); + + /** + * The salt is used to initialize the PBE (password Based Encrpytion) algorithm. + */ + private static final byte[] DEFAULT_SALT = { + (byte) 0xc7, (byte) 0x73, (byte) 0x21, (byte) 0x8c, (byte) 0x7e, (byte) 0xc8, (byte) 0xee, (byte) 0x99 + }; + + /** + * The prefix we insert onto any data we encrypt so that we can tell if it is encrpyted later and therefore decrypt + * it + */ + @SuppressWarnings("nls") + public static final String ENCRYPTED_VALUE_PREFIX = "enc:"; + + /** + * The instance of the encryption utility object + */ + private static EncryptionTool instance = null; + + /** + * The iteration count used to initialize the PBE algorithm and to generate the key spec + */ + private static final int ITERATION_COUNT = 20; + + /** + * The logger for this class. + */ + private static final Logger LOG = LoggerFactory.getLogger(EncryptionTool.class); + + /** + * The secret passphrase (PBE) that we use to perform encryption and decryption. The algorithm we are using is a + * symmetrical cipher. + */ + private static char[] secret = { + 'C', '_', 'z', 'l', '!', 'K', '!', '4', '?', 'O', 'z', 'E', 'K', 'E', '>', 'U', 'R', '/', '%', 'Y', '\\', 'f', + 'b', '"', 'e', 'n', '{', '"', 'l', 'U', 'F', '+', 'E', '\'', 'R', 'T', 'p', '1', 'V', '4', 'l', 'a', '9', 'w', + 'v', '5', 'Z', '#', 'i', 'V', '"', 'd', 'l', '!', 'L', 'M', 'g', 'L', 'Q', '{', 'v', 'v', 'K', 'V' + }; + + /** + * The algorithm to encrypt and decrpyt data is "Password (or passphrase) Based Encryption with Message Digest #5 + * and the Data Encryption Standard", i.e., PBEWithMD5AndDES. + */ + @SuppressWarnings("nls") + private static final String SECURITY_ALGORITHM = "PBEWITHMD5AND256BITAES";// "PBEWithMD5AndDES"; + + /** + * The decryption cipher object + */ + private Cipher decryptCipher = null; + + /** + * The encryption cipher object + */ + private Cipher encryptCipher = null; + + private BasicTextEncryptor encryptor; + + /** + * Get an instance of the EncryptionTool + * + * @return The encryption tool to be used + */ + public static final EncryptionTool getInstance() { + if (instance == null) { + synchronized (lock) { + if (instance == null) { + instance = new EncryptionTool(); + } + } + } + return instance; + } + + /** + * Create the EncryptionTool instance + */ + @SuppressWarnings("nls") + private EncryptionTool() { + String out = "Found the following security algorithms:"; + for (Provider p : Security.getProviders()) { + for (Service s : p.getServices()) { + String algo = s.getAlgorithm(); + out += + String.format("%n -Algorithm [ %s ] in provider [ %s ] and service [ %s ]", algo, p.getName(), + s.getClassName()); + } + } + LOG.debug(out); + } + + /** + * Decrypt the provided encrypted text + * + * @param cipherText + * THe cipher text to be decrypted. If the ciphertext is not encrypted, then it is returned as is. + * @return the clear test of the (possibly) encrypted value. The original value if the string is not encrypted. + */ + @SuppressWarnings("nls") + public synchronized String decrypt(String cipherText) { + if (isEncrypted(cipherText)) { + String encValue = cipherText.substring(ENCRYPTED_VALUE_PREFIX.length()); + byte[] plainByte = Base64.decodeBase64(encValue.getBytes()); + byte[] decryptByte = xorWithSecret(plainByte); + return new String(decryptByte); + } else { + return cipherText; + } + + } + + /** + * Encrypt the provided clear text + * + * @param clearText + * The clear text to be encrypted + * @return the encrypted text. If the clear text is empty (null or zero length), then an empty string is returned. + * If the clear text is already encrypted, it is not encrypted again and is returned as is. Otherwise, the + * clear text is encrypted and returned. + */ + @SuppressWarnings("nls") + public synchronized String encrypt(String clearText) { + if (clearText != null) { + byte[] encByte = xorWithSecret(clearText.getBytes()); + String encryptedValue = new String(Base64.encodeBase64(encByte)); + return ENCRYPTED_VALUE_PREFIX + encryptedValue; + } else { + return null; + } + } + + /** + * Is a value encrypted? A value is considered to be encrypted if it begins with the + * {@linkplain #ENCRYPTED_VALUE_PREFIX encrypted value prefix}. + * + * @param value + * the value to check. + * @return true/false; + */ + private static boolean isEncrypted(final String value) { + return value != null && value.startsWith(ENCRYPTED_VALUE_PREFIX); + } + + /** + * XORs the input byte array with the secret key, padding 0x0 to the end of the secret key if the input is longer + * and returns a byte array the same size as input + * + * @param inp + * The byte array to be XORed with secret + * @return A byte array the same size as inp or null if input is null. + */ + private byte[] xorWithSecret(byte[] inp) { + if (inp == null) { + return null; + } + + byte[] secretBytes = new String(secret).getBytes(); + int size = inp.length; + + byte[] out = new byte[size]; + for (int i = 0; i < size; i++) { + out[i] = (byte) ((inp[i]) ^ (secretBytes[i % secretBytes.length])); + } + return out; + } + +} diff --git a/appc-core/appc-common-bundle/java/org/onap/appc/encryption/HexHelper.java b/appc-core/appc-common-bundle/java/org/onap/appc/encryption/HexHelper.java new file mode 100644 index 000000000..9e975f84d --- /dev/null +++ b/appc-core/appc-common-bundle/java/org/onap/appc/encryption/HexHelper.java @@ -0,0 +1,162 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * 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.appc.encryption; + +import java.util.HashMap; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * HexHelper utility used for encryption/decryption + */ +public final class HexHelper { + + @SuppressWarnings({ + "javadoc", "nls" + }) + public static final String CM_PATH = "@(#) [viewpath]/[item]"; + + @SuppressWarnings({ + "nls", "javadoc" + }) + public static final String CM_PROJECT = "@(#) [environment] [baseline]"; + + @SuppressWarnings({ + "javadoc", "nls" + }) + public static final String CM_VERSION = "@(#) [version] [crtime]"; + + private static final char[] HEX_TABLE = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', + 'E', 'F'}; + + /** + * The logger for this class. + */ + private static final Logger LOG = LoggerFactory.getLogger(HexHelper.class); + + private static Map textToHex; + + static { + textToHex = new HashMap<>(); + textToHex.put('0', 0); + textToHex.put('1', 1); + textToHex.put('2', 2); + textToHex.put('3', 3); + textToHex.put('4', 4); + textToHex.put('5', 5); + textToHex.put('6', 6); + textToHex.put('7', 7); + textToHex.put('8', 8); + textToHex.put('9', 9); + textToHex.put('A', 10); + textToHex.put('B', 11); + textToHex.put('C', 12); + textToHex.put('D', 13); + textToHex.put('E', 14); + textToHex.put('F', 15); + } + + /** + * Default private constructor prevents instantiation + */ + private HexHelper() { + // no-op + } + + /** + * Converts an array of bytes to the equivalent string representation using hexadecimal notation + * + * @param bytes The bytes to be converted to a hexadecimal string + * @return The string representation + */ + public static String convertBytesToHex(byte[] bytes) throws EncryptionException{ + + if (bytes == null) + throw new EncryptionException("Given byte array is null"); + + StringBuilder builder = new StringBuilder(bytes.length * 2); + for (byte aByte : bytes) { + char tempChar; + // Get the first 4 bits (high) Do bitwise logical AND to get rid of + // low nibble. Shift results to right by 4 and get char + // representation + tempChar = HEX_TABLE[(aByte & 0xf0) >>> 4]; + builder.append(tempChar); + + // Get the last 4 bits (low) Do bitwise logical AND to get rid of + // high nibble. Get char representation + tempChar = HEX_TABLE[aByte & 0x0f]; + builder.append(tempChar); + } + return builder.toString(); + } + + /** + * Converts a hexadecimal string representation of a binary value to an array of bytes + * + * @param hexValue The hex representation string to be converted + * @return The array of bytes that contains the binary value + */ + @SuppressWarnings("nls") + public static byte[] convertHexToBytes(String hexValue) throws EncryptionException { + + if (hexValue ==null) + throw new EncryptionException("Given hex value is null"); + + byte[] bytes; + byte high; + byte low; + char hexChar; + + StringBuilder builder = new StringBuilder(hexValue.toUpperCase()); + if (builder.length() % 2 != 0) { + LOG.warn("Invalid HEX value length. The length of the value has to be a multiple of 2." + + " Prepending '0' value."); + builder.insert(0, '0'); + } + int hexLength = builder.length(); + int byteLength = hexLength / 2; + + bytes = new byte[byteLength]; + try { + for (int index = 0; index < hexLength; index += 2) { + hexChar = builder.charAt(index); + high = textToHex.get(hexChar).byteValue(); + high = (byte) (high << 4); + hexChar = builder.charAt(index + 1); + low = textToHex.get(hexChar).byteValue(); + high = (byte) (high | low); + bytes[index / 2] = high; + } + } + catch (NullPointerException e){ + LOG.error("Given string contains not hexadecimal values", e); + throw new EncryptionException("Given string contains not hexadecimal values"); + } + + return bytes; + } +} diff --git a/appc-core/appc-common-bundle/java/org/onap/appc/exceptions/APPCException.java b/appc-core/appc-common-bundle/java/org/onap/appc/exceptions/APPCException.java new file mode 100644 index 000000000..90dfa6710 --- /dev/null +++ b/appc-core/appc-common-bundle/java/org/onap/appc/exceptions/APPCException.java @@ -0,0 +1,105 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * 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.appc.exceptions; + +/** + * This is a base class for all APPC defined exceptions. + */ + +public class APPCException extends Exception { + + /** + * + */ + private static final long serialVersionUID = 1L; + + /** + * Constructs a new exception with null as its detail message. The cause is not initialized, and may subsequently be + * initialized by a call to initCause. + */ + public APPCException() { + } + + /** + * Constructs a new exception with the specified detail message. The cause is not initialized, and may subsequently + * be initialized by a call to initCause. + * + * @param message + * the detail message. The detail message is saved for later retrieval by the getMessage() method. + */ + public APPCException(String message) { + super(message); + } + + /** + * Constructs a new exception with the specified cause and a detail message of (cause==null ? null : + * cause.toString()) (which typically contains the class and detail message of cause). This constructor is useful + * for exceptions that are little more than wrappers for other throwables (for example, + * java.security.PrivilegedActionException). + * + * @param cause + * the cause (which is saved for later retrieval by the getCause() method). (A null value is permitted, + * and indicates that the cause is nonexistent or unknown.) + */ + public APPCException(Throwable cause) { + super(cause); + } + + /** + * + Constructs a new exception with the specified detail message and cause. + *

+ * Note that the detail message associated with cause is not automatically incorporated in this exception's detail + * message. + *

+ * + * @param message + * the detail message (which is saved for later retrieval by the getMessage() method). + * @param cause + * the cause (which is saved for later retrieval by the getCause() method). (A null value is permitted, + * and indicates that the cause is nonexistent or unknown.) + */ + public APPCException(String message, Throwable cause) { + super(message, cause); + } + + /** + * + Constructs a new exception with the specified detail message, cause, suppression enabled or disabled, and + * writable stack trace enabled or disabled. + * + * @param message + * the detail message. + * @param cause + * the cause. (A null value is permitted, and indicates that the cause is nonexistent or unknown.) + * @param enableSuppression + * whether or not suppression is enabled or disabled + * @param writableStackTrace + * whether or not the stack trace should be writable + */ + public APPCException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + +} diff --git a/appc-core/appc-common-bundle/java/org/onap/appc/exceptions/InvalidInputException.java b/appc-core/appc-common-bundle/java/org/onap/appc/exceptions/InvalidInputException.java new file mode 100644 index 000000000..32a6cdef5 --- /dev/null +++ b/appc-core/appc-common-bundle/java/org/onap/appc/exceptions/InvalidInputException.java @@ -0,0 +1,30 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * 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.appc.exceptions; + +public class InvalidInputException extends Exception { + public InvalidInputException(String message){ + super(message); + } +} diff --git a/appc-core/appc-common-bundle/java/org/onap/appc/exceptions/InvalidStateException.java b/appc-core/appc-common-bundle/java/org/onap/appc/exceptions/InvalidStateException.java new file mode 100644 index 000000000..61a8770a7 --- /dev/null +++ b/appc-core/appc-common-bundle/java/org/onap/appc/exceptions/InvalidStateException.java @@ -0,0 +1,30 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * 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.appc.exceptions; + +public class InvalidStateException extends Exception { + public InvalidStateException(String message) { + super(message); + } +} diff --git a/appc-core/appc-common-bundle/java/org/onap/appc/exceptions/UnknownProviderException.java b/appc-core/appc-common-bundle/java/org/onap/appc/exceptions/UnknownProviderException.java new file mode 100644 index 000000000..d23c8027b --- /dev/null +++ b/appc-core/appc-common-bundle/java/org/onap/appc/exceptions/UnknownProviderException.java @@ -0,0 +1,107 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * 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.appc.exceptions; + +/** + * This exception indicates that the named provider could not be found or was unidentifiable. + */ +public class UnknownProviderException extends APPCException { + + /** + * + */ + private static final long serialVersionUID = 1L; + + /** + * Constructs a new exception with null as its detail message. The cause is not initialized, and may subsequently be + * initialized by a call to initCause. + */ + public UnknownProviderException() { + } + + /** + * Constructs a new exception with the specified detail message. The cause is not initialized, and may subsequently + * be initialized by a call to initCause. + * + * @param message + * the detail message. The detail message is saved for later retrieval by the getMessage() method. + */ + public UnknownProviderException(String message) { + super(message); + } + + /** + * Constructs a new exception with the specified cause and a detail message of (cause==null ? null : + * cause.toString()) (which typically contains the class and detail message of cause). This constructor is useful + * for exceptions that are little more than wrappers for other throwables (for example, + * java.security.PrivilegedActionException). + * + * @param cause + * the cause (which is saved for later retrieval by the getCause() method). (A null value is permitted, + * and indicates that the cause is nonexistent or unknown.) + */ + public UnknownProviderException(Throwable cause) { + super(cause); + } + + /** + * + Constructs a new exception with the specified detail message and cause. + *

+ * Note that the detail message associated with cause is not automatically incorporated in this exception's detail + * message. + *

+ * + * @param message + * the detail message (which is saved for later retrieval by the getMessage() method). + * @param cause + * the cause (which is saved for later retrieval by the getCause() method). (A null value is permitted, + * and indicates that the cause is nonexistent or unknown.) + */ + public UnknownProviderException(String message, Throwable cause) { + super(message, cause); + } + + /** + * + Constructs a new exception with the specified detail message, cause, suppression enabled or disabled, and + * writable stack trace enabled or disabled. + * + * @param message + * the detail message. + * @param cause + * the cause. (A null value is permitted, and indicates that the cause is nonexistent or unknown.) + * @param enableSuppression + * whether or not suppression is enabled or disabled + * @param writableStackTrace + * whether or not the stack trace should be writable + */ + public UnknownProviderException(String message, + Throwable cause, + boolean enableSuppression, + boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + +} diff --git a/appc-core/appc-common-bundle/java/org/onap/appc/i18n/Msg.java b/appc-core/appc-common-bundle/java/org/onap/appc/i18n/Msg.java new file mode 100644 index 000000000..ad696cdef --- /dev/null +++ b/appc-core/appc-common-bundle/java/org/onap/appc/i18n/Msg.java @@ -0,0 +1,847 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * 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.appc.i18n; + +import com.att.eelf.i18n.EELFResolvableErrorEnum; +import com.att.eelf.i18n.EELFResourceManager; + +/** + * The messages issued by APPC components. + *

+ * This message definition is shared by all APPC components. + *

+ * + */ +@SuppressWarnings("nls") +public enum Msg implements EELFResolvableErrorEnum { + + /** + * ECOMP Application Controller (APP-C) initialization started at {0} + */ + CONFIGURATION_STARTED, + + /** + * Prior configuration has been cleared + */ + CONFIGURATION_CLEARED, + + /** + * Loading configuration properties from file "{0}" + */ + LOADING_CONFIGURATION_OVERRIDES, + + /** + * Configuration defaults loaded from resource file "{0}" + */ + LOADING_DEFAULTS, + + /** + * No default property resource "{0}" was found! + */ + NO_DEFAULTS_FOUND, + + /** + * Property "{0}" ="{1}" + */ + PROPERTY_VALUE, + + /** + * No configuration file named [{0}] was found on the configuration search path [{1}]. \ If a configuration file + * should have been loaded, check the file name and search path specified. CDP will proceed using the \ default + * values and command-line overrides (if any). + */ + NO_OVERRIDE_PROPERTY_FILE_LOADED, + + /** + * Searching path "{0}" for configuration settings "{1}" + */ + SEARCHING_CONFIGURATION_OVERRIDES, + + /** + * Loading application-specific override properties + */ + LOADING_APPLICATION_OVERRIDES, + + /** + * No application-specific override properties were provided! + */ + NO_APPLICATION_OVERRIDES, + + /** + * Merging system properties into configuration + */ + MERGING_SYSTEM_PROPERTIES, + + /** + * Setting property "{0}={1}" in system properties + */ + SETTING_SPECIAL_PROPERTY, + + /** + * Loading resource bundle "{0}" + */ + LOADING_RESOURCE_BUNDLE, + + /** + * Logging has already been initialized, check the container logging definitions to ensure they represent your + * desired logging configuration. + */ + LOGGING_ALREADY_INITIALIZED, + + /** + * Searching path "{0}" for log configuration file "{1}" + */ + SEARCHING_LOG_CONFIGURATION, + + /** + * Loading default logging configuration from system resource file "{0}" + */ + LOADING_DEFAULT_LOG_CONFIGURATION, + + /** + * No log configuration could be found or defaulted! + */ + NO_LOG_CONFIGURATION, + + /** + * An unsupported logging framework is bound to SLF4J. Only Logback or Log4J are supported. + */ + UNSUPPORTED_LOGGING_FRAMEWORK, + + /** + * Loading logging configuration from file "{0}" + */ + LOADING_LOG_CONFIGURATION, + + /** + * Provider {0} cannot be found or cannot be resolved to a valid provider. + */ + UNKNOWN_PROVIDER, + + /** + * Server name "{0}" with id "{1}" in tenant "{2}" and region "{3}" did not change state within the alloted time. + * Current state is "{4}" and the desired state(s) are "{5}" + */ + SERVER_STATE_CHANGE_TIMEOUT, + + /** + * Server name "{0}" with id "{1}" in tenant "{2}" has a state of deleted and cannot be {3}. + */ + SERVER_DELETED, + + /** + * Server name "{0}" with id "{1}" in tenant "{2}" has an unknown state of "{3}". + */ + UNKNOWN_SERVER_STATE, + + /** + * {0} component {1} is being initialized... + */ + COMPONENT_INITIALIZING, + + /** + * {0} component {1} has completed initialization + */ + COMPONENT_INITIALIZED, + + /** + * {0} component {1} is terminating... + */ + COMPONENT_TERMINATING, + + /** + * {0} component {1} has terminated + */ + COMPONENT_TERMINATED, + + /** + * Operation {0} is not supported or implemented at this time. + */ + IAAS_ADAPTER_UNSUPPORTED_OPERATION, + + /** + * Operation {0} called. Input document:\n{1} + */ + IAAS_ADAPTER_RPC_CALLED, + + /** + * Unable to locate the {0} service in the OSGi container + */ + NO_SERVICE_FOUND, + + /** + * Dump of context parameters for module {0}, RPC {1}, and version {2} + */ + CONTEXT_PARAMETERS_DISPLAY, + + /** + * Response properties from execution of module '{0}', RPC '{1}', and version '{2}' are: + */ + RESPONSE_PARAMETERS_DISPLAY, + + /** + * Service {0}:{1} was provided a null (empty) or invalid argument, '{2}' = '{3}' + */ + NULL_OR_INVALID_ARGUMENT, + + /** + * Service {0}:{1} is processing service '{2}' with request id '{3}' + */ + PROCESSING_REQUEST, + + /** + * Service {0}:{1} received request for service '{2}' but that service is invalid or unknown. + */ + INVALID_SERVICE_REQUEST, + + /** + * {0} registering service {1} using class {2} + */ + REGISTERING_SERVICE, + + /** + * {0} unregistering service {1} + */ + UNREGISTERING_SERVICE, + + /** + * {0} IAAS Adapter initializing provider {1} as {2} + */ + LOADING_PROVIDER_DEFINITIONS, + + /** + * {0} IAAS Adapter restart of server requested + */ + RESTARTING_SERVER, + + /** + * {0} IAAS Adapter rebuild of server requested + */ + REBUILDING_SERVER, + + /** + * {0} IAAS Adapter migrate of server requested + */ + MIGRATING_SERVER, + + /** + * {0} IAAS Adapter evacuate of server requested + */ + EVACUATING_SERVER, + + /** + * {0} IAAS Adapter create snapshot of server requested + */ + SNAPSHOTING_SERVER, + + /** + * {0} IAAS Adapter look for server requested + */ + LOOKING_SERVER_UP, + + /** + * {0} IAAS Adapter cannot perform requested service, VM url '{1}' is invalid + */ + INVALID_SELF_LINK_URL, + + /** + * Located server '{0}' on tenant '{1}' and in state '{2}' + */ + SERVER_FOUND, + + /** + * No server found in provider with self-link URL [{0}] + */ + SERVER_NOT_FOUND, + + /** + * Exception {0} was caught attempting {1} of server [{2}] on tenant [{3}] + */ + SERVER_OPERATION_EXCEPTION, + + /** + * One or more properties for [{0}] are missing, null, or empty. They are: + */ + MISSING_REQUIRED_PROPERTIES, + + /** + * The server [{0}] (id={1}) in tenant {2} is in error state, {3} is not allowed + */ + SERVER_ERROR_STATE, + + /** + * The image {0} could not be located for {1} + */ + IMAGE_NOT_FOUND, + + /** + * Time out waiting for {0} with name {1} (and id {2}) to reach one of {3} states, current state is {4} + */ + STATE_CHANGE_TIMEOUT, + + /** + * Exception {0} waiting for {1} with name {2} (and id {3}) to reach one of {4} states, current state is {5} + * cause={6} + */ + STATE_CHANGE_EXCEPTION, + + /** + * Server {0} is being stopped... + */ + STOP_SERVER, + + /** + * Server {0} is being started... + */ + START_SERVER, + + /** + * Server {0} is being resumed... + */ + RESUME_SERVER, + + /** + * Server {0} is being unpaused... + */ + UNPAUSE_SERVER, + + /** + * Server {0} is being rebuilt... + */ + REBUILD_SERVER, + + /** + * Connection to provider {0} at identity {1} using tenant name {2} (id {3}) failed, reason={4}, retrying in {5} + * seconds, attempt {6} of {7}. + */ + CONNECTION_FAILED_RETRY, + + /** + * Connection to provider {0} at service {1} failed after all retry attempts. + */ + CONNECTION_FAILED, + + /** + * {0} IAAS Adapter stop server requested + */ + STOPPING_SERVER, + + /** + * {0} IAAS Adapter start server requested + */ + STARTING_SERVER, + + /** + * Server {0} (id {1}) failed to rebuild, reason {2} + */ + REBUILD_SERVER_FAILED, + + /** + * Application {0} graph {1} response did not set the {2} parameter. This parameter is required for synchronization + * with the controller. Absence of this parameter is assumed to be a failure. Please correct the DG. + */ + PARAMETER_IS_MISSING, + + /** + * Application {0} graph {1} did not set parameter {2} to a valid numeric value ({3}). Please correct the DG. + */ + PARAMETER_NOT_NUMERIC, + + /** + * Application {0} graph {1} completed with failure: error code = {2}, message = {3} + */ + DG_FAILED_RESPONSE, + + /** + * Application {0} received exception {1} attempting to call graph {2}, exception message = {3} + */ + EXCEPTION_CALLING_DG, + + /** + * Application {0} was unable to locate graph {1} + */ + GRAPH_NOT_FOUND, + + /** + * Application {0} graph {1} responded with {3} properties + */ + DEBUG_GRAPH_RESPONSE_HEADER, + + /** + * {0}:{1} - {2} = {3} + */ + DEBUG_GRAPH_RESPONSE_DETAIL, + + /** + * Application {0} request {1} was supplied a property '{2}' with the value '{3}' that does not meet the required + * form(s): + */ + INVALID_REQUIRED_PROPERTY, + + /** + * Server {0} (id {1}) failed to migrate during {2} phase, reason {3} + */ + MIGRATE_SERVER_FAILED, + + /** + * Server {0} (id {1}) failed to evacuate, reason {2} + */ + EVACUATE_SERVER_FAILED, + + /** + * Server {0} evacuate from host {1} to host {2} failed during the rebuild on host {2}, reason {3} + */ + EVACUATE_SERVER_REBUILD_FAILED, + + /** + * APP-C instance is too busy + */ + APPC_TOO_BUSY, + + /** + * Concurrent access to server "{0}" + */ + VF_SERVER_BUSY, + + /** + * Server "{0}" does not support command "{1}" in the current state "{2}" + */ + VF_ILLEGAL_COMMAND, + + /** + * Server "{0}" cannot handle command "{1}" because of its doubtful state + */ + VF_UNDEFINED_STATE, + + /** + * No resource found with ID "{0}" in A&AI system + */ + APPC_NO_RESOURCE_FOUND, + + /** + * The request "{0}" for server "{1}" has exceeded its TTL limit of "{3}" seconds + */ + APPC_EXPIRED_REQUEST, + + /** + * Workflow for vnfType = "{0}" and command = "{1}" not found. + */ + APPC_WORKFLOW_NOT_FOUND, + + /** + * Null vnfId and command provided + */ + APPC_INVALID_INPUT, + + /** + * Operation '{0}' for VNF type '{1}' from Source '{2}' with RequestID '{3}' was started at '{4}' and ended at '{5}' + * with status code '{6}' + */ + APPC_AUDIT_MSG, + + /** + * APP-C is unable to communicate with A&AI + */ + AAI_CONNECTION_FAILED, + + /** + * APP-C is unable to update COMPONENT_ID {0} to {1} for reason {2} + */ + AAI_UPDATE_FAILED, + + /** + * APP-C is unable to retrieve VF/VFC {0} data for Transaction ID{1}as a result of A&AI communication failure or its + * internal error. + */ + AAI_GET_DATA_FAILED, + + /** + * A&AI at identity {0} using VNF_ID {1} failed, reason={2}, retrying in {3} seconds, attempt {4} of {5} + */ + AAI_CONNECTION_FAILED_RETRY, + + /** + * APP-C is unable to delete COMPONENT_ID {0} for reason {1} + */ + AAI_DELETE_FAILED, + + /** + * APP-C is unable to query AAI for VNF_ID {0} + */ + AAI_QUERY_FAILED, + + /** + * VNF {0} is configured + */ + VNF_CONFIGURED, + + /** + * VNF {0} is being configured + */ + VNF_CONFIGURATION_STARTED, + + /** + * VNF {0} configuration failed for reason {1} + */ + VNF_CONFIGURATION_FAILED, + + /** + * VNF {0} is being tested + */ + VNF_TEST_STARTED, + + /** + * VNF {0} was tested + */ + VNF_TESTED, + + /** + * VNF {0} test failed for reason {1} + */ + VNF_TEST_FAILED, + + /** + * VNF {0} test failed for reason {1} + */ + VNF_NOT_FOUND, + + /** + * VNF {0} Healthcheck operation failed for reason {1} + */ + VNF_HEALTHCECK_FAILED, + + /** + * VM {0} Healthcheck operation failed for reason {1} + */ + VM_HEALTHCECK_FAILED, + + /** + * Server {0} (id {1}) failed to stop during {2} phase, reason {3} + */ + STOP_SERVER_FAILED, + + /** + * Server {0} (id {1}) failed to terminate during {2} phase, reason {3} + */ + TERMINATE_SERVER_FAILED, + + /** + * {0} IAAS Adapter terminate server requested + */ + TERMINATING_SERVER, + + /** + * Server {0} is being terminated... + */ + TERMINATE_SERVER, + + /** + * Migrate {0} finished with status {1}. Start Time: {2}. End Time: {3}. Request ID: {4}. Reason:{5}... + */ + MIGRATE_COMPLETE, + + /** + * Restart {0} finished with status {1}. Start Time: {2}. End Time: {3}. Request ID: {4}. Reason:{5}... + */ + RESTART_COMPLETE, + + /** + * Rebuild {0} finished with status {1}. Start Time: {2}. End Time: {3}. Request ID: {4}. Reason:{5}... + */ + REBUILD_COMPLETE, + + /** + * Located stack '{0}' on tenant '{1}' and in state '{2}' + */ + STACK_FOUND, + + /** + * {0} IAAS Adapter terminate stack requested + */ + + TERMINATING_STACK, + + /** + * stack {0} is being terminated... + */ + TERMINATE_STACK, + /** + * No stack found in provider with self-link URL [{0}] + */ + + STACK_NOT_FOUND, + + /** + * Exception {0} was caught attempting {1} of stack [{2}] on tenant [{3}] + */ + STACK_OPERATION_EXCEPTION, + + /** + * Stack {0} (id {1}) failed to terminate during {2} phase, reason {3} + */ + + TERMINATE_STACK_FAILED, + + /** + * Exception {0} was caught attempting to close provider context for {1}. + */ + + CLOSE_CONTEXT_FAILED, + + /** + * {0} IAAS Adapter snapshoting stack + */ + SNAPSHOTING_STACK, + + /** + * Stack {0} snapshoted, snapshot ID = [{1}]. + */ + STACK_SNAPSHOTED, + + /** + * {0} IAAS Adapter restoring stack + */ + RESTORING_STACK, + + /** + * Stack {0} is restored to snapshot {1}. + */ + STACK_RESTORED, + + /** + * {0} IAAS Adapter checking server + */ + CHECKING_SERVER, + + /** + * Parameter {0} is missing in svc request of {1}. + */ + MISSING_PARAMETER_IN_REQUEST, + + /** + * Cannot establish connection to server {0} port {1} with user {2}. + */ + CANNOT_ESTABLISH_CONNECTION, + + /** + * Operation '{0}' for VNF type '{1}' from Source '{2}' with RequestID '{3}' on '{4}' with action '{5}' + * ended in {6}ms with result '{7}' + */ + APPC_METRIC_MSG, + + /** + * Parsing failied for{0} + */ + INPUT_PAYLOAD_PARSING_FAILED, + + /** + * Error occurred for due to {0} + */ + APPC_EXCEPTION, + + /** + * SSH Data Exception occurred due to {0} + */ + SSH_DATA_EXCEPTION, + + /** + * Json processing exception occurred due to {0} + */ + JSON_PROCESSING_EXCEPTION, + + /** + * Operation {0} succeed for {1} + */ + SUCCESS_EVENT_MESSAGE, + + /** + * Dependency model not found for VNF type {0} due to {1} + */ + DEPENDENCY_MODEL_NOT_FOUND, + + /** + * Invalid Dependency model for VNF Type {0} due to {1} + */ + INVALID_DEPENDENCY_MODEL, + + /** + * Failed to retrieve VNFC DG + */ + FAILURE_RETRIEVE_VNFC_DG, + + /** + * Network check for Server {0} failed for Port {1} + * + */ + SERVER_NETWORK_ERROR, + + /** + * Hypervisor check for Server {0} failed. Status is DOWN or UNKNOWN + * + */ + HYPERVISOR_DOWN_ERROR, + + /** + * Unable to determine Hypervisor status for Server {0}. failed. + * + */ + HYPERVISOR_STATUS_UKNOWN, + + /** + * Hypervisor Network check for Server {0} failed. Not reachable by APPC + * + */ + HYPERVISOR_NETWORK_ERROR, + + /** + * Restart application operation failed on server : {0}, reason {1} + */ + APPLICATION_RESTART_FAILED, + + /** + * Start application operation failed on server : {0}, reason {1} + */ + APPLICATION_START_FAILED, + + /** + * Start application operation failed on server : {0}, reason {1} + */ + APPLICATION_STOP_FAILED, + + /** + * Application on server {0} is being restarted... + */ + RESTART_APPLICATION, + + /** + * Application on server {0} is being started... + */ + START_APPLICATION, + + /** + * Application on server {0} is being started... + */ + STOP_APPLICATION, + + /** + * APPC LCM operations are disabled + */ + LCM_OPERATIONS_DISABLED, + + /** + * Application {0} received exception {1} while attempting to execute oam operation {2}, exception message = {3}|\ + */ + OAM_OPERATION_EXCEPTION, + + /** + * Application {0} is {1} + */ + OAM_OPERATION_ENTERING_MAINTENANCE_MODE, + + /** + * Application {0} is in {1} + */ + OAM_OPERATION_MAINTENANCE_MODE, + + /** + * Application {0} is {1} + */ + OAM_OPERATION_STARTING, + + /** + * Application {0} is {1} + */ + OAM_OPERATION_STARTED, + + /** + * Application {0} is {1} + */ + OAM_OPERATION_STOPPING, + + /** + * Application {0} is {1} + */ + OAM_OPERATION_STOPPED, + /** + * A {1} API is not allowed when {0} is in the {2} state + */ + INVALID_STATE_TRANSITION, + + /** + * Application {0} was unable to find the Request Handler service + */ + REQUEST_HANDLER_UNAVAILABLE, + + /** + * Application {0} is {1} + */ + OAM_OPERATION_RESTARTING, + + /** + * Application {0} is {1} for restart + */ + OAM_OPERATION_RESTARTED, + + /** + * {0} + */ + OAM_OPERATION_INVALID_INPUT, + + ATTACHINGVOLUME_SERVER, + + DETTACHINGVOLUME_SERVER, + + /** + * Unsupported identity service version, unable to retrieve ServiceCatalog + * for identity service {0} + */ + IAAS_UNSUPPORTED_IDENTITY_SERVICE, + + /** + * Sftp data transfer failed on connection to host {0} with user {1} for {2} operation, reason : {3} + */ + SFTP_TRANSFER_FAILED, + + /** + * Ssh session with host {0} has timed out during command {1} execution + */ + SSH_CONNECTION_TIMEOUT, + + /** + *Connecting to image service {0}failed + */ + IMAGE_SERVICE_FAILED, + + /** + * Could not configure existing ssh session, reason: {0} + */ + SSH_SESSION_CONFIG_ERROR + ; + /* + * Static initializer to ensure the resource bundles for this class are loaded... + */ + static { + EELFResourceManager.loadMessageBundle("org/onap/appc/i18n/MessageResources"); + } +} diff --git a/appc-core/appc-common-bundle/java/org/onap/appc/logging/LoggingConstants.java b/appc-core/appc-common-bundle/java/org/onap/appc/logging/LoggingConstants.java new file mode 100644 index 000000000..4178f6bcc --- /dev/null +++ b/appc-core/appc-common-bundle/java/org/onap/appc/logging/LoggingConstants.java @@ -0,0 +1,109 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * 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.appc.logging; + +/** + * Constant definition of logging + */ +public class LoggingConstants { + private LoggingConstants() { + throw new IllegalAccessError("LoggingConstants"); + } + + /** + * Constants of MDC property keys + */ + public static class MDCKeys { + private MDCKeys() { + throw new IllegalAccessError("MDCKeys"); + } + + public static final String ERROR_CODE = "ErrorCode"; + public static final String ERROR_DESCRIPTION = "ErrorDescription"; + public static final String STATUS_CODE = "StatusCode"; + public static final String RESPONSE_CODE = "ResponseCode"; + public static final String RESPONSE_DESCRIPTION = "ResponseDescription"; + public static final String TARGET_ENTITY = "TargetEntity"; + public static final String TARGET_SERVICE_NAME = "TargetServiceName"; + public static final String PARTNER_NAME = "PartnerName"; + public static final String SERVER_NAME = "ServerName"; + public static final String BEGIN_TIMESTAMP = "BeginTimestamp"; + public static final String END_TIMESTAMP = "EndTimestamp"; + public static final String ELAPSED_TIME = "ElapsedTime"; + public static final String CLASS_NAME = "ClassName"; + public static final String TARGET_VIRTUAL_ENTITY = "TargetVirtualEntity"; + } + + /** + * Constants of status code values + */ + public static class StatusCodes { + private StatusCodes() { + throw new IllegalAccessError("StatusCodes"); + } + public static final String COMPLETE = "COMPLETE"; + public static final String ERROR = "ERROR"; + } + + /** + * Constants of APPC target names + */ + public static class TargetNames { + private TargetNames() { + throw new IllegalAccessError("TargetNames"); + } + public static final String APPC = "APPC"; + public static final String AAI = "A&AI"; + public static final String DB = "DataBase"; + public static final String APPC_PROVIDER = "APPC Provider"; + public static final String APPC_OAM_PROVIDER = "APPC OAM Provider"; + public static final String STATE_MACHINE = "StateMachine"; + public static final String WORKFLOW_MANAGER = "WorkflowManager"; + public static final String REQUEST_VALIDATOR = "RequestValidator"; + public static final String LOCK_MANAGER = "LockManager"; + public static final String REQUEST_HANDLER = "RequestHandler"; + } + + /** + * Constants of targeted service names + */ + public static class TargetServiceNames { + private TargetServiceNames() { + throw new IllegalAccessError("TargetServiceNames"); + } + + /** + * Constants of AAI service names + */ + public static class AAIServiceNames { + private AAIServiceNames() { + throw new IllegalAccessError("AAIServiceNames"); + } + public static final String QUERY = "query"; + public static final String GET_VNF_DATA = "getVnfData"; + } + + } + +} diff --git a/appc-core/appc-common-bundle/java/org/onap/appc/logging/LoggingUtils.java b/appc-core/appc-common-bundle/java/org/onap/appc/logging/LoggingUtils.java new file mode 100644 index 000000000..cfb98da9e --- /dev/null +++ b/appc-core/appc-common-bundle/java/org/onap/appc/logging/LoggingUtils.java @@ -0,0 +1,235 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * 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.appc.logging; + +import org.onap.appc.i18n.Msg; +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import com.att.eelf.i18n.EELFResolvableErrorEnum; +import com.att.eelf.i18n.EELFResourceManager; +import org.slf4j.MDC; +import static com.att.eelf.configuration.Configuration.MDC_KEY_REQUEST_ID; +import static com.att.eelf.configuration.Configuration.MDC_SERVICE_NAME; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.Date; +import java.util.TimeZone; + +/** + * Logging utilities + */ +public class LoggingUtils { + + private static final EELFLogger errorLogger = EELFManager.getInstance().getErrorLogger(); + private static final EELFLogger auditLogger = EELFManager.getInstance().getAuditLogger(); + private static final EELFLogger metricLogger = EELFManager.getInstance().getMetricsLogger(); + + private LoggingUtils() { + throw new IllegalAccessError("LoggingUtils"); + } + + public static void logErrorMessage(String errorCode, String errorDescription, + String targetEntity, String targetServiceName, String additionalMessage, + String className) { + logError(errorCode, errorDescription, targetEntity, targetServiceName, additionalMessage, + className); + } + + public static void logErrorMessage(String targetEntity, String targetServiceName, + String additionalMessage, String className) { + logError("", "", targetEntity, targetServiceName, additionalMessage, className); + } + + public static void logErrorMessage(String targetServiceName, String additionalMessage, + String className) { + logError("", "", LoggingConstants.TargetNames.APPC, targetServiceName, additionalMessage, + className); + } + + private static void logError(String errorCode, String errorDescription, String targetEntity, + String targetServiceName, String additionalMessage, String className) { + populateErrorLogContext(errorCode, errorDescription, targetEntity, targetServiceName, + className); + errorLogger.error(additionalMessage == null ? "" : additionalMessage); + cleanErrorLogContext(); + } + + public static void logAuditMessage(Instant beginTimeStamp, Instant endTimeStamp, String code, + String responseDescription, String className) { + populateAuditLogContext(beginTimeStamp, endTimeStamp, code, responseDescription, className); + auditLogger.info(EELFResourceManager.format(Msg.APPC_AUDIT_MSG, MDC.get(MDC_SERVICE_NAME), + MDC.get(LoggingConstants.MDCKeys.TARGET_VIRTUAL_ENTITY), + MDC.get(LoggingConstants.MDCKeys.PARTNER_NAME), MDC.get(MDC_KEY_REQUEST_ID), + MDC.get(LoggingConstants.MDCKeys.BEGIN_TIMESTAMP), + MDC.get(LoggingConstants.MDCKeys.END_TIMESTAMP), + MDC.get(LoggingConstants.MDCKeys.RESPONSE_CODE))); + cleanAuditErrorContext(); + } + + public static void auditInfo(Instant beginTimeStamp, Instant endTimeStamp, String code, + String responseDescription, String className, EELFResolvableErrorEnum resourceId, + String... arguments) { + populateAuditLogContext(beginTimeStamp, endTimeStamp, code, responseDescription, className); + auditLogger.info(resourceId, arguments); + cleanAuditErrorContext(); + } + + public static void auditWarn(Instant beginTimeStamp, Instant endTimeStamp, String code, + String responseDescription, String className, EELFResolvableErrorEnum resourceId, + String... arguments) { + populateAuditLogContext(beginTimeStamp, endTimeStamp, code, responseDescription, className); + auditLogger.warn(resourceId, arguments); + cleanAuditErrorContext(); + } + + public static void logMetricsMessage(Instant beginTimeStamp, Instant endTimeStamp, + String targetEntity, String targetServiceName, String statusCode, String responseCode, + String responseDescription, String className) { + populateMetricLogContext(beginTimeStamp, endTimeStamp, targetEntity, targetServiceName, + statusCode, responseCode, responseDescription, className); + metricLogger.info(EELFResourceManager.format(Msg.APPC_METRIC_MSG, MDC.get(MDC_SERVICE_NAME), + MDC.get(LoggingConstants.MDCKeys.TARGET_VIRTUAL_ENTITY), + MDC.get(LoggingConstants.MDCKeys.PARTNER_NAME), MDC.get(MDC_KEY_REQUEST_ID), + MDC.get(LoggingConstants.MDCKeys.TARGET_ENTITY), + MDC.get(LoggingConstants.MDCKeys.TARGET_SERVICE_NAME), + MDC.get(LoggingConstants.MDCKeys.ELAPSED_TIME), + MDC.get(LoggingConstants.MDCKeys.STATUS_CODE))); + cleanMetricContext(); + } + + private static void populateAuditLogContext(Instant beginTimeStamp, Instant endTimeStamp, + String code, String responseDescription, String className) { + populateTimeContext(beginTimeStamp, endTimeStamp); + MDC.put(LoggingConstants.MDCKeys.RESPONSE_CODE, code); + MDC.put(LoggingConstants.MDCKeys.STATUS_CODE, "100".equals(code) || "400".equals(code) + ? LoggingConstants.StatusCodes.COMPLETE : LoggingConstants.StatusCodes.ERROR); + MDC.put(LoggingConstants.MDCKeys.RESPONSE_DESCRIPTION, + responseDescription != null ? responseDescription : ""); + MDC.put(LoggingConstants.MDCKeys.CLASS_NAME, className != null ? className : ""); + } + + private static void cleanAuditErrorContext() { + cleanTimeContext(); + MDC.remove(LoggingConstants.MDCKeys.STATUS_CODE); + MDC.remove(LoggingConstants.MDCKeys.RESPONSE_CODE); + MDC.remove(LoggingConstants.MDCKeys.RESPONSE_DESCRIPTION); + MDC.remove(LoggingConstants.MDCKeys.CLASS_NAME); + } + + private static void populateErrorLogContext(String errorCode, String errorDescription, + String targetEntity, String targetServiceName, String className) { + populateErrorContext(errorCode, errorDescription); + populateTargetContext(targetEntity, targetServiceName != null ? targetServiceName : ""); + MDC.put(LoggingConstants.MDCKeys.CLASS_NAME, className != null ? className : ""); + } + + private static void cleanErrorLogContext() { + cleanErrorContext(); + cleanTargetContext(); + MDC.remove(LoggingConstants.MDCKeys.CLASS_NAME); + } + + private static void populateMetricLogContext(Instant beginTimeStamp, Instant endTimeStamp, + String targetEntity, String targetServiceName, String statusCode, String responseCode, + String responseDescription, String className) { + populateTimeContext(beginTimeStamp, endTimeStamp); + populateTargetContext(targetEntity, targetServiceName); + populateResponseContext(statusCode, responseCode, responseDescription); + MDC.put(LoggingConstants.MDCKeys.CLASS_NAME, className != null ? className : ""); + } + + private static void cleanMetricContext() { + cleanTimeContext(); + cleanTargetContext(); + cleanResponseContext(); + MDC.remove(LoggingConstants.MDCKeys.CLASS_NAME); + } + + private static void populateTargetContext(String targetEntity, String targetServiceName) { + MDC.put(LoggingConstants.MDCKeys.TARGET_ENTITY, targetEntity != null ? targetEntity : ""); + MDC.put(LoggingConstants.MDCKeys.TARGET_SERVICE_NAME, + targetServiceName != null ? targetServiceName : ""); + } + + private static void cleanTargetContext() { + MDC.remove(LoggingConstants.MDCKeys.TARGET_ENTITY); + MDC.remove(LoggingConstants.MDCKeys.TARGET_SERVICE_NAME); + } + + private static void populateTimeContext(Instant beginTimeStamp, Instant endTimeStamp) { + String beginTime = ""; + String endTime = ""; + String elapsedTime = ""; + + if (beginTimeStamp != null && endTimeStamp != null) { + elapsedTime = String.valueOf(ChronoUnit.MILLIS.between(beginTimeStamp, endTimeStamp)); + beginTime = generateTimestampStr(beginTimeStamp); + endTime = generateTimestampStr(endTimeStamp); + } + + MDC.put(LoggingConstants.MDCKeys.BEGIN_TIMESTAMP, beginTime); + MDC.put(LoggingConstants.MDCKeys.END_TIMESTAMP, endTime); + MDC.put(LoggingConstants.MDCKeys.ELAPSED_TIME, elapsedTime); + } + + private static String generateTimestampStr(Instant timeStamp) { + DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX"); + TimeZone tz = TimeZone.getTimeZone("UTC"); + df.setTimeZone(tz); + return df.format(Date.from(timeStamp)); + } + + private static void cleanTimeContext() { + MDC.remove(LoggingConstants.MDCKeys.BEGIN_TIMESTAMP); + MDC.remove(LoggingConstants.MDCKeys.END_TIMESTAMP); + MDC.remove(LoggingConstants.MDCKeys.ELAPSED_TIME); + } + + private static void populateResponseContext(String statusCode, String responseCode, + String responseDescription) { + MDC.put(LoggingConstants.MDCKeys.STATUS_CODE, statusCode != null ? statusCode : ""); + MDC.put(LoggingConstants.MDCKeys.RESPONSE_CODE, responseCode); + MDC.put(LoggingConstants.MDCKeys.RESPONSE_DESCRIPTION, + responseDescription != null ? responseDescription : ""); + } + + private static void cleanResponseContext() { + MDC.remove(LoggingConstants.MDCKeys.STATUS_CODE); + MDC.remove(LoggingConstants.MDCKeys.RESPONSE_CODE); + MDC.remove(LoggingConstants.MDCKeys.RESPONSE_DESCRIPTION); + } + + private static void populateErrorContext(String errorCode, String errorDescription) { + MDC.put(LoggingConstants.MDCKeys.ERROR_CODE, errorCode); + MDC.put(LoggingConstants.MDCKeys.ERROR_DESCRIPTION, errorDescription); + } + + private static void cleanErrorContext() { + MDC.remove(LoggingConstants.MDCKeys.ERROR_CODE); + MDC.remove(LoggingConstants.MDCKeys.ERROR_DESCRIPTION); + } + +} diff --git a/appc-core/appc-common-bundle/java/org/onap/appc/metadata/MetadataService.java b/appc-core/appc-common-bundle/java/org/onap/appc/metadata/MetadataService.java new file mode 100644 index 000000000..dfaa462aa --- /dev/null +++ b/appc-core/appc-common-bundle/java/org/onap/appc/metadata/MetadataService.java @@ -0,0 +1,31 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * 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.appc.metadata; + +import org.onap.appc.metadata.objects.DependencyModelIdentifier; + + +public interface MetadataService { + String getVnfModel(DependencyModelIdentifier modelIdentifier); +} diff --git a/appc-core/appc-common-bundle/java/org/onap/appc/metadata/impl/MetadataServiceImpl.java b/appc-core/appc-common-bundle/java/org/onap/appc/metadata/impl/MetadataServiceImpl.java new file mode 100644 index 000000000..70d2e6363 --- /dev/null +++ b/appc-core/appc-common-bundle/java/org/onap/appc/metadata/impl/MetadataServiceImpl.java @@ -0,0 +1,123 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * 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.appc.metadata.impl; + +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import org.onap.ccsdk.sli.core.dblib.DbLibService; + +import javax.sql.rowset.CachedRowSet; + +import org.onap.appc.cache.MetadataCache; +import org.onap.appc.cache.impl.MetadataCacheFactory; +import org.onap.appc.metadata.MetadataService; +import org.onap.appc.metadata.objects.DependencyModelIdentifier; + +import java.sql.SQLException; +import java.util.ArrayList; + + +public class MetadataServiceImpl implements MetadataService { + + private DbLibService dbLibService; + + private static final EELFLogger logger = EELFManager.getInstance().getLogger(MetadataServiceImpl.class); + + private MetadataCache cache; + + public MetadataServiceImpl() { + initialize(); + } + + private void initialize() { + cache = MetadataCacheFactory.getInstance().getMetadataCache(); + // TODO initialze dbLibService + } + + public void setDbLibService(DbLibService dbLibService) { + this.dbLibService = dbLibService; + } + + void setCache(MetadataCache cache) { + this.cache = cache; + } + + @Override + public String getVnfModel(DependencyModelIdentifier modelIdentifier) { + logger.debug("Reading Vnf Model data from cache for vnfType : " + modelIdentifier.getVnfType() + + " and catalog version : " + modelIdentifier.getCatalogVersion()); + String vnfModel = cache.getObject(modelIdentifier); + if (vnfModel == null || vnfModel.length() == 0) { + logger.debug("Vnf Model not available in cache. Reading from database."); + vnfModel = readVnfModel(modelIdentifier); + if (vnfModel != null && vnfModel.length() > 0) { + logger.debug("Adding retrieved Vnf Model to cache."); + addVnfModel(modelIdentifier, vnfModel); + } + } + return vnfModel; + } + + private void addVnfModel(DependencyModelIdentifier modelIdentifier, String vnfModel) { + cache.putObject(modelIdentifier, vnfModel); + } + + private String readVnfModel(DependencyModelIdentifier modelIdentifier) { + + logger.debug("Reading Vnf Model data from database for RESOURCE_NAME : " + modelIdentifier.getVnfType() + + " and RESOURCE_VERSION : " + modelIdentifier.getCatalogVersion()); + StringBuilder query = new StringBuilder(); + String vnfModel = null; + query.append("SELECT ARTIFACT_CONTENT FROM sdnctl.ASDC_ARTIFACTS WHERE RESOURCE_NAME = ? "); + ArrayList argList = new ArrayList<>(); + argList.add(modelIdentifier.getVnfType()); + + if (modelIdentifier.getCatalogVersion() == null) { + query.append(" ORDER BY SUBSTRING_INDEX(RESOURCE_VERSION, '.', 1)*1 DESC , " + + "SUBSTRING_INDEX(SUBSTRING_INDEX(RESOURCE_VERSION, '.', 2),'.', -1) *1 DESC , " + + "SUBSTRING_INDEX(RESOURCE_VERSION, '.', -1)*1 DESC ;"); + } else { + query.append("AND RESOURCE_VERSION = ? ;"); + argList.add(modelIdentifier.getCatalogVersion()); + } + try { + final CachedRowSet data = dbLibService.getData(query.toString(), argList, "sdnctl"); + if (data.first()) { + vnfModel = data.getString("ARTIFACT_CONTENT"); + if (vnfModel == null || vnfModel.isEmpty()) { + logger.error("Invalid dependency model for vnf type : " + modelIdentifier.getVnfType() + + " and catalog version : " + modelIdentifier.getCatalogVersion()); + throw new RuntimeException("Invalid or Empty VNF Model"); + } + logger.debug("Retrieved Vnf Model : " + vnfModel); + } else { + logger.warn("VNF Model not found in datastore for RESOURCE_NAME : " + modelIdentifier.getVnfType() + + " AND RESOURCE_VERSION : " + modelIdentifier.getCatalogVersion()); + } + } catch (SQLException e) { + throw new RuntimeException("Database error occurred"); + } + return vnfModel; + } +} diff --git a/appc-core/appc-common-bundle/java/org/onap/appc/metadata/objects/DependencyModelIdentifier.java b/appc-core/appc-common-bundle/java/org/onap/appc/metadata/objects/DependencyModelIdentifier.java new file mode 100644 index 000000000..070e200c4 --- /dev/null +++ b/appc-core/appc-common-bundle/java/org/onap/appc/metadata/objects/DependencyModelIdentifier.java @@ -0,0 +1,98 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * 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.appc.metadata.objects; + +/** + * Object of identifier for dependency model. Currently uses VNF type and catalog version + */ +public class DependencyModelIdentifier { + static final String TO_STRING_FORMAT = + "DependencyModelIdentifier : vnfType = %s , catalogVersion = %s"; + static final int prime = 31; + + private String vnfType; + private String catalogVersion; + + /** + * Constructor + * + * @param vnfType String of the VNF type + * @param catalogVersion String of the catalog version + */ + public DependencyModelIdentifier(String vnfType, String catalogVersion) { + this.vnfType = vnfType; + this.catalogVersion = catalogVersion; + } + + @Override + public int hashCode() { + int result = 1; + result = result * prime + (this.vnfType == null ? 0 : this.vnfType.hashCode()); + result = result * prime + + (this.catalogVersion == null ? 0 : this.catalogVersion.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (!(obj instanceof DependencyModelIdentifier)) { + return false; + } + + DependencyModelIdentifier modelIdentifier = (DependencyModelIdentifier) obj; + if (this.vnfType == null) { + if (modelIdentifier.vnfType != null) { + return false; + } + } else if (!this.vnfType.equals(modelIdentifier.vnfType)) { + return false; + } + + if (this.catalogVersion == null) { + if (modelIdentifier.catalogVersion != null) { + return false; + } + } else if (!this.catalogVersion.equals(modelIdentifier.catalogVersion)) { + return false; + } + return true; + } + + @Override + public String toString() { + return String.format(TO_STRING_FORMAT, vnfType, catalogVersion); + } + + public String getVnfType() { + return vnfType; + } + + public String getCatalogVersion() { + return catalogVersion; + } + +} diff --git a/appc-core/appc-common-bundle/java/org/onap/appc/pool/Allocator.java b/appc-core/appc-common-bundle/java/org/onap/appc/pool/Allocator.java new file mode 100644 index 000000000..9992e89ed --- /dev/null +++ b/appc-core/appc-common-bundle/java/org/onap/appc/pool/Allocator.java @@ -0,0 +1,47 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * 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.appc.pool; + +import java.io.Closeable; + +/** + * This interface is used to supply an object that will be called by the pool manager whenever a new widget must be + * allocated. + * @param + * The generic type that we are caching. + */ + +public interface Allocator { + + /** + * Allocate an object of type and return it to the pool + * + * @param pool + * The pool that the object is to be allocated to + * @return An object of type T + */ + T allocate(Pool pool); +} diff --git a/appc-core/appc-common-bundle/java/org/onap/appc/pool/CacheManagement.java b/appc-core/appc-common-bundle/java/org/onap/appc/pool/CacheManagement.java new file mode 100644 index 000000000..d0051a8e0 --- /dev/null +++ b/appc-core/appc-common-bundle/java/org/onap/appc/pool/CacheManagement.java @@ -0,0 +1,34 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * 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.appc.pool; + +public interface CacheManagement { + + /** + * @return The object that is actually being wrapped and cached + */ + Object getWrappedObject(); + +} diff --git a/appc-core/appc-common-bundle/java/org/onap/appc/pool/CachedElement.java b/appc-core/appc-common-bundle/java/org/onap/appc/pool/CachedElement.java new file mode 100644 index 000000000..e9b2ffeae --- /dev/null +++ b/appc-core/appc-common-bundle/java/org/onap/appc/pool/CachedElement.java @@ -0,0 +1,214 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * 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.appc.pool; + +import java.io.Closeable; +import java.io.IOException; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * This class is used as a "wrapper" for any closeable elements that are cached in a pool. It is + * implemented as a dynamic proxy, so that it appears to be the same class of object to the client + * as the interface being cached. The generic type being cached MUST be an interface. + * + * @param The generic type that we create a cached element for. This type is used to wrap + * instances of this type and expose access to the {@link java.io.Closeable} interface by + * using a dynamic proxy. + */ + +public class CachedElement + implements Closeable, InvocationHandler, CacheManagement { + + /** + * The pool that is managing this cached element + */ + private Pool pool; + + /** + * The element that we are caching in the pool + */ + private T element; + + /** + * A thread-safe atomic indicator that tells us that the wrapped element has been released to + * the pool already, and not to do it again. + */ + private AtomicBoolean released = new AtomicBoolean(false); + + /** + * Create a new instance of a cached element dynamic proxy for use in the pool. + *

+ * This returns an instance of the proxy to the caller that appears to be the same interface(s) + * as the object being cached. The dynamic proxy then intercepts all open and close semantics + * and directs that element to the pool. + *

+ *

+ * If the object being proxied does not implement the {@link CacheManagement} interface, then + * that interface is added to the dynamic proxy being created. This interface is actually + * implemented by the invocation handler (this object) for the proxy and allows direct access to + * the wrapped object inside the proxy. + *

+ * + * @param pool The pool that we are caching these elements within + * @param element The element actually being cached + * @param interfaces The interface list of interfaces the element must implement (usually one) + * @return The dynamic proxy + */ + @SuppressWarnings("unchecked") + public static T newInstance(Pool pool, T element, + Class[] interfaces) { + ClassLoader cl = element.getClass().getClassLoader(); + CachedElement ce = new CachedElement<>(pool, element); + boolean found = false; + for (Class intf : interfaces) { + if (intf.getName().equals(CacheManagement.class.getName())) { + found = true; + break; + } + } + + int length = found ? interfaces.length : interfaces.length + 1; + Class[] proxyInterfaces = new Class[length]; + System.arraycopy(interfaces, 0, proxyInterfaces, 0, interfaces.length); + + if (!found) { + proxyInterfaces[interfaces.length] = CacheManagement.class; + } + + return (T) Proxy.newProxyInstance(cl, proxyInterfaces, ce); + } + + /** + * Construct a cached element and assign it to the pool as a free element + * + * @param pool The pool that the element will be managed within + * @param element The element we are caching + */ + @SuppressWarnings("unchecked") + public CachedElement(Pool pool, T element) { + this.pool = pool; + this.element = element; + + try { + pool.release((T) this); + } catch (PoolDrainedException e) { + e.printStackTrace(); + } + } + + /** + * This method delegates the close call to the actual wrapped element. + *

+ * NOTE: This is not the same method that is called by the dynamic proxy. This method is in + * place to satisfy the signature of the {@link java.io.Closeable} interface. If it were to be + * called directly, then we will delegate the close to the underlying context. However, when the + * cached element is called as a synamic proxy, entry is in the + * {@link #invoke(Object, Method, Object[])} method. + *

+ * + * @see java.io.Closeable#close() + */ + @Override + public void close() throws IOException { + element.close(); + } + + /** + * This method is the magic part of dynamic proxies. When the caller makes a method call based + * on the interface being proxied, this method is given control. This informs us of the method + * and arguments of the call. The object reference is that of the dynamic proxy itself, which is + * us. + *

+ * Here we will check to see if the user is trying to close the "element" (the dynamic proxy + * acts like the wrapped element). If he is, then we don't really close it, but instead release + * the element that we are wrapping back to the free pool. Once this has happened, we mark the + * element as "closed" (from the perspective of this dynamic proxy) so that we wont try to + * release it again. + *

+ *

+ * If the method is the equals method then we assume that we are comparing the + * cached element in one dynamic proxy to the cached element in another. We execute the + * comparison between the cached elements, and not the dynamic proxies themselves. This + * preserves the allusion to the caller that the dynamic proxy is the object being wrapped. + *

+ *

+ * For convenience, we also implement the getWrappedObject method so that the + * dynamic proxy can be called to obtain the actual wrapped object if desired. Note, to use this + * method, the caller would have to invoke it through reflection. + *

+ *

+ * If the method being invoked is not one that we intercept, then we simply delegate that method + * onto the wrapped object. + *

+ * + * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, + * java.lang.Object[]) + */ + @SuppressWarnings({"unchecked", "nls"}) + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Exception { + Object result = null; + + switch (method.getName()) { + case "close": + if (released.compareAndSet(false, true)) { + if (!pool.isDrained()) { + pool.release((T) proxy); + } + } + break; + case "equals": + CacheManagement cm = (CacheManagement) proxy; + T other = (T) cm.getWrappedObject(); + result = element.equals(other); + break; + case "getWrappedObject": + return element; + default: + result = method.invoke(element, args); + break; + } + + return result; + } + + /** + * This method is used to be able to access the wrapped object underneath the dynamic proxy + * + * @see org.onap.appc.pool.CacheManagement#getWrappedObject() + */ + @Override + public T getWrappedObject() { + return element; + } + + @SuppressWarnings("nls") + @Override + public String toString() { + return element == null ? "null" : element.toString(); + } +} diff --git a/appc-core/appc-common-bundle/java/org/onap/appc/pool/Destructor.java b/appc-core/appc-common-bundle/java/org/onap/appc/pool/Destructor.java new file mode 100644 index 000000000..6c7e7d418 --- /dev/null +++ b/appc-core/appc-common-bundle/java/org/onap/appc/pool/Destructor.java @@ -0,0 +1,46 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * 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.appc.pool; + +import java.io.Closeable; + +/** + * @param + * The generic type we are caching + */ + +public interface Destructor { + + /** + * Called to destroy the object when it is no longer being used by the pool + * + * @param obj + * The object to be destroyed + * @param pool + * The pool that the object is being removed from + */ + void destroy(T obj, Pool pool); +} diff --git a/appc-core/appc-common-bundle/java/org/onap/appc/pool/Pool.java b/appc-core/appc-common-bundle/java/org/onap/appc/pool/Pool.java new file mode 100644 index 000000000..1faf0fe42 --- /dev/null +++ b/appc-core/appc-common-bundle/java/org/onap/appc/pool/Pool.java @@ -0,0 +1,371 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * 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.appc.pool; + +import java.io.Closeable; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Deque; +import java.util.List; +import java.util.ListIterator; +import java.util.Properties; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +/** + * This class is used to manage a pool of things. + *

+ * The class is parameterized so that the type of objects maintained in the pool is definable by some provided type. + * This type must implement the Comparable interface so that it can be managed in the pool. + *

+ * + * @param + * The type of element being pooled + */ + +public class Pool { + private Deque free; + private List allocated; + private int minPool; + private int maxPool; + private Allocator allocator; + private Destructor destructor; + private ReadWriteLock lock; + private AtomicBoolean drained; + private Properties properties; + + /** + * Create the pool + * + * @param minPool + * The minimum size of the pool + * @param maxPool + * The maximum size of the pool, set to zero (0) for unbounded + * @throws PoolSpecificationException + * If the minimum size is less than 0, or if the max size is non-zero and less than the min size. + */ + public Pool(int minPool, int maxPool) throws PoolSpecificationException { + + if (minPool < 0) { + throw new PoolSpecificationException(String.format("The minimum pool size must be a " + + "positive value or zero, %d is not valid.", minPool)); + } + if (maxPool != 0 && maxPool < minPool) { + throw new PoolSpecificationException(String.format("The maximum pool size must be a " + + "positive value greater than the minimum size, or zero. %d is not valid.", maxPool)); + } + + this.minPool = minPool; + this.maxPool = maxPool; + + properties = new Properties(); + free = new ArrayDeque(); + allocated = new ArrayList(); + lock = new ReentrantReadWriteLock(); + drained = new AtomicBoolean(false); + } + + /** + * Returns the amount of objects on the free collection + * + * @return The number of objects on the free collection + */ + public int getFreeSize() { + Lock readLock = lock.readLock(); + readLock.lock(); + try { + return free.size(); + } finally { + readLock.unlock(); + } + } + + /** + * Returns the value for a specified property of this pool, if defined. + * + * @param key + * The key of the desired property + * @return The value of the property, or null if not defined + */ + public String getProperty(String key) { + return properties.getProperty(key); + } + + /** + * Sets the value of the specified property or replaces it if it already exists + * + * @param key + * The key of the property to be set + * @param value + * The value to set the property to + */ + public void setProperty(String key, String value) { + properties.setProperty(key, value); + } + + /** + * @return The properties object for the pool + */ + public Properties getProperties() { + return properties; + } + + /** + * Returns the number of objects that are currently allocated + * + * @return The allocate collection size + */ + public int getAllocatedSize() { + Lock readLock = lock.readLock(); + readLock.lock(); + try { + return allocated.size(); + } finally { + readLock.unlock(); + } + } + + /** + * @return the value of allocator + */ + public Allocator getAllocator() { + return allocator; + } + + /** + * @param allocator + * the value for allocator + */ + public void setAllocator(Allocator allocator) { + this.allocator = allocator; + } + + /** + * @return the value of destructor + */ + public Destructor getDestructor() { + return destructor; + } + + /** + * @return the value of minPool + */ + public int getMinPool() { + return minPool; + } + + /** + * @return the value of maxPool + */ + public int getMaxPool() { + return maxPool; + } + + /** + * @param destructor + * the value for destructor + */ + public void setDestructor(Destructor destructor) { + this.destructor = destructor; + } + + /** + * Drains the pool, releasing and destroying all pooled objects, even if they are currently allocated. + */ + public void drain() { + if (drained.compareAndSet(false, true)) { + Lock writeLock = lock.writeLock(); + writeLock.lock(); + try { + int size = getAllocatedSize(); + /* + * We can't use the "release" method call here because we are modifying the list we are iterating + */ + ListIterator it = allocated.listIterator(); + while (it.hasNext()) { + T obj = it.next(); + it.remove(); + free.addFirst(obj); + } + size = getFreeSize(); + trim(size); + } finally { + writeLock.unlock(); + } + } + } + + /** + * Returns an indication if the pool has been drained + * + * @return True indicates that the pool has been drained. Once a pool has been drained, it can no longer be used. + */ + public boolean isDrained() { + return drained.get(); + } + + /** + * Reserves an object of type T from the pool for the caller and returns it + * + * @return The object of type T to be used by the caller + * @throws PoolExtensionException + * If the pool cannot be extended + * @throws PoolDrainedException + * If the caller is trying to reserve an element from a drained pool + */ + @SuppressWarnings("unchecked") + public T reserve() throws PoolExtensionException, PoolDrainedException { + if (isDrained()) { + throw new PoolDrainedException("The pool has been drained and cannot be used."); + } + + T obj = null; + Lock writeLock = lock.writeLock(); + writeLock.lock(); + try { + int freeSize = getFreeSize(); + int allocatedSize = getAllocatedSize(); + + if (freeSize == 0) { + if (allocatedSize == 0) { + extend(minPool == 0 ? 1 : minPool); + } else if (allocatedSize >= maxPool && maxPool > 0) { + throw new PoolExtensionException(String.format("Unable to add " + + "more elements, pool is at maximum size of %d", maxPool)); + } else { + extend(1); + } + } + + obj = free.removeFirst(); + allocated.add(obj); + } finally { + writeLock.unlock(); + } + + /* + * Now that we have the real object, lets wrap it in a dynamic proxy so that we can intercept the close call and + * just return the context to the free pool. obj.getClass().getInterfaces(). We need to find ALL interfaces that + * the object (and all superclasses) implement and have the proxy implement them too + */ + Class cls = obj.getClass(); + Class[] array; + List> interfaces = new ArrayList>(); + while (!cls.equals(Object.class)) { + array = cls.getInterfaces(); + for (Class item : array) { + if (!interfaces.contains(item)) { + interfaces.add(item); + } + } + cls = cls.getSuperclass(); + } + array = new Class[interfaces.size()]; + array = interfaces.toArray(array); + return CachedElement.newInstance(this, obj, array); + } + + /** + * releases the allocated object back to the free pool to be used by another request. + * + * @param obj + * The object to be returned to the pool + * @throws PoolDrainedException + * If the caller is trying to release an element to a drained pool + */ + public void release(T obj) throws PoolDrainedException { + if (isDrained()) { + throw new PoolDrainedException("The pool has been drained and cannot be used."); + } + Lock writeLock = lock.writeLock(); + writeLock.lock(); + try { + if (allocated.remove(obj)) { + free.addFirst(obj); + } + } finally { + writeLock.unlock(); + } + } + + /** + * Extend the free pool by some number of elements + * + * @param count + * The number of elements to add to the pool + * @throws PoolExtensionException + * if the pool cannot be extended because no allocator has been specified. + */ + private void extend(int count) throws PoolExtensionException { + if (allocator == null) { + throw new PoolExtensionException(String.format("Unable to extend pool " + + "because no allocator has been specified")); + } + Lock writeLock = lock.writeLock(); + writeLock.lock(); + try { + for (int index = 0; index < count; index++) { + T obj = allocator.allocate(this); + if (obj == null) { + throw new PoolExtensionException( + "The allocator failed to allocate a new context to extend the pool."); + } + free.push(obj); + } + } finally { + writeLock.unlock(); + } + } + + /** + * Used to trim the free collection by some specified number of elements, or the free element count, whichever is + * less. The elements are removed from the end of the free element deque, thus trimming the oldest elements first. + * + * @param count + * The number of elements to trim + */ + private void trim(int count) { + Lock writeLock = lock.writeLock(); + writeLock.lock(); + try { + int trimCount = count; + if (getFreeSize() < count) { + trimCount = getFreeSize(); + } + for (int i = 0; i < trimCount; i++) { + T obj = free.removeLast(); + if (destructor != null) { + destructor.destroy(obj, this); + } + } + } finally { + writeLock.unlock(); + } + } +} diff --git a/appc-core/appc-common-bundle/java/org/onap/appc/pool/PoolDrainedException.java b/appc-core/appc-common-bundle/java/org/onap/appc/pool/PoolDrainedException.java new file mode 100644 index 000000000..08313669f --- /dev/null +++ b/appc-core/appc-common-bundle/java/org/onap/appc/pool/PoolDrainedException.java @@ -0,0 +1,50 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * 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.appc.pool; + +/** + * This exception is thrown whenever an attempt is made to access a pool of resources where the pool has been drained. + * Once drained, the pool is no longer usable. + * + */ +public class PoolDrainedException extends PoolException { + + /** + * + */ + private static final long serialVersionUID = 1L; + + /** + * PoolDrainedException constructor + * + * @param msg + * The error message + */ + public PoolDrainedException(String msg) { + super(msg); + } + +} diff --git a/appc-core/appc-common-bundle/java/org/onap/appc/pool/PoolException.java b/appc-core/appc-common-bundle/java/org/onap/appc/pool/PoolException.java new file mode 100644 index 000000000..9b22eeecf --- /dev/null +++ b/appc-core/appc-common-bundle/java/org/onap/appc/pool/PoolException.java @@ -0,0 +1,91 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * 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.appc.pool; + +/** + * A pool exception is a specialization of checked exceptions that define various pool abnormal states or requests. + * + */ +public class PoolException extends Exception { + /** + * + */ + private static final long serialVersionUID = 1L; + + /** + * PoolException constructor + */ + public PoolException() { + } + + /** + * PoolException constructor + * + * @param message + * The error message + */ + public PoolException(String message) { + super(message); + } + + /** + * PoolException constructor + * + * @param cause + * The cause of the exception + */ + public PoolException(Throwable cause) { + super(cause); + } + + /** + * PoolException constructor + * + * @param message + * The error message + * @param cause + * The cause of the exception + */ + public PoolException(String message, Throwable cause) { + super(message, cause); + } + + /** + * PoolException constructor + * + * @param message + * The error message + * @param cause + * The cause of the exception + * @param enableSuppression + * whether or not suppression is enabled or disabled + * @param writableStackTrace + * whether or not the stack trace should be writable + */ + public PoolException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/appc-core/appc-common-bundle/java/org/onap/appc/pool/PoolExtensionException.java b/appc-core/appc-common-bundle/java/org/onap/appc/pool/PoolExtensionException.java new file mode 100644 index 000000000..9daaca1b6 --- /dev/null +++ b/appc-core/appc-common-bundle/java/org/onap/appc/pool/PoolExtensionException.java @@ -0,0 +1,48 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * 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.appc.pool; + +/** + * An error occurred trying to extend the pool + * + */ +public class PoolExtensionException extends PoolException { + + /** + * + */ + private static final long serialVersionUID = 1L; + + /** + * PoolExtensionException constructor + * + * @param msg + * The error message + */ + public PoolExtensionException(String msg) { + super(msg); + } +} diff --git a/appc-core/appc-common-bundle/java/org/onap/appc/pool/PoolSpecificationException.java b/appc-core/appc-common-bundle/java/org/onap/appc/pool/PoolSpecificationException.java new file mode 100644 index 000000000..d1f2189b5 --- /dev/null +++ b/appc-core/appc-common-bundle/java/org/onap/appc/pool/PoolSpecificationException.java @@ -0,0 +1,49 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * 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.appc.pool; + +/** + * This exception is thrown whenever the pool is not specified correctly + * + */ +public class PoolSpecificationException extends PoolException { + + /** + * + */ + private static final long serialVersionUID = 1L; + + /** + * PoolSpecificationException constructor + * + * @param msg + * The error message + */ + public PoolSpecificationException(String msg) { + super(msg); + } + +} diff --git a/appc-core/appc-common-bundle/java/org/onap/appc/rest/client/RestClientInvoker.java b/appc-core/appc-common-bundle/java/org/onap/appc/rest/client/RestClientInvoker.java new file mode 100644 index 000000000..41a0a85a9 --- /dev/null +++ b/appc-core/appc-common-bundle/java/org/onap/appc/rest/client/RestClientInvoker.java @@ -0,0 +1,258 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * 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.appc.rest.client; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.MalformedURLException; +import java.net.Socket; +import java.net.URL; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import org.apache.commons.codec.binary.Base64; +import org.apache.http.HttpHeaders; +import org.apache.http.HttpResponse; +import org.apache.http.HttpVersion; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.conn.ClientConnectionManager; +import org.apache.http.conn.scheme.PlainSocketFactory; +import org.apache.http.conn.scheme.Scheme; +import org.apache.http.conn.scheme.SchemeRegistry; +import org.apache.http.conn.ssl.SSLSocketFactory; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; +import org.apache.http.params.BasicHttpParams; +import org.apache.http.params.HttpParams; +import org.apache.http.params.HttpProtocolParams; +import org.apache.http.protocol.HTTP; +import org.onap.appc.exceptions.APPCException; +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; + +@SuppressWarnings("deprecation") +public class RestClientInvoker { + + private static final EELFLogger LOG = EELFManager.getInstance().getLogger(RestClientInvoker.class); + private static final String OPERATION_HTTPS = "https"; + private static final String OPERATION_APPLICATION_JSON = " application/json"; + private static final String BASIC = "Basic "; + + private URL url = null; + private String basicAuth = null; + + public RestClientInvoker(URL url) { + this.url = url; + } + + /** + * Sets the basic authentication header for the given user and password. If either entry is null + * then does not set basic auth + * + * @param user The user with optional domain name (for AAF) + * @param password The password for the user + */ + public void setAuthentication(String user, String password) { + if (user != null && password != null) { + String authStr = user + ":" + password; + basicAuth = new String(Base64.encodeBase64(authStr.getBytes())); + } + } + + public HttpResponse doPost(String path, String body) throws APPCException { + HttpPost post; + + try { + + URL postUrl = new URL(url.getProtocol(), url.getHost(), url.getPort(), path); + post = new HttpPost(postUrl.toExternalForm()); + post.setHeader(HttpHeaders.CONTENT_TYPE, OPERATION_APPLICATION_JSON); + post.setHeader(HttpHeaders.ACCEPT, OPERATION_APPLICATION_JSON); + + if (basicAuth != null) { + post.setHeader(HttpHeaders.AUTHORIZATION, BASIC + basicAuth); + } + + StringEntity entity = new StringEntity(body); + entity.setContentType(OPERATION_APPLICATION_JSON); + post.setEntity(new StringEntity(body)); + } catch (MalformedURLException | UnsupportedEncodingException e) { + throw new APPCException(e); + } + HttpClient client = getHttpClient(); + + try { + return client.execute(post); + } catch (IOException e) { + throw new APPCException(e); + } + } + + /** + * This is Generic method that can be used to perform REST Put operation + * + * @param path - path for put + * @param body - payload for put action which will be sent as request body. + * @return - HttpResponse object which is returned from put REST call. + * @throws APPCException when error occurs + */ + public HttpResponse doPut(String path, String body) throws APPCException { + HttpPut put; + try { + URL putUrl = new URL(url.getProtocol(), url.getHost(), url.getPort(), path); + put = new HttpPut(putUrl.toExternalForm()); + put.setHeader(HttpHeaders.CONTENT_TYPE, OPERATION_APPLICATION_JSON); + put.setHeader(HttpHeaders.ACCEPT, OPERATION_APPLICATION_JSON); + + if (basicAuth != null) { + put.setHeader(HttpHeaders.AUTHORIZATION, BASIC + basicAuth); + } + + StringEntity entity = new StringEntity(body); + entity.setContentType(OPERATION_APPLICATION_JSON); + put.setEntity(new StringEntity(body)); + } catch (UnsupportedEncodingException | MalformedURLException e) { + throw new APPCException(e); + } + + HttpClient client = getHttpClient(); + + try { + return client.execute(put); + } catch (IOException e) { + throw new APPCException(e); + } + } + + public HttpResponse doGet(String path) throws APPCException { + HttpGet get; + try { + URL getUrl = new URL(url.getProtocol(), url.getHost(), url.getPort(), path); + get = new HttpGet(getUrl.toExternalForm()); + get.setHeader(HttpHeaders.CONTENT_TYPE, OPERATION_APPLICATION_JSON); + get.setHeader(HttpHeaders.ACCEPT, OPERATION_APPLICATION_JSON); + + if (basicAuth != null) { + get.setHeader(HttpHeaders.AUTHORIZATION, BASIC + basicAuth); + } + + } catch (Exception e) { + throw new APPCException(e); + } + + try (CloseableHttpClient client = getHttpClient()) { + return client.execute(get); + } catch (IOException e) { + throw new APPCException(e); + } + } + + private CloseableHttpClient getHttpClient() throws APPCException { + switch (url.getProtocol()) { + case OPERATION_HTTPS: + return createHttpsClient(); + case "http": + return new DefaultHttpClient(); + default: + throw new APPCException("The url did not start with http[s]"); + } + } + + + private CloseableHttpClient createHttpsClient() { + try { + KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); + trustStore.load(null, null); + MySSLSocketFactory sf = new MySSLSocketFactory(trustStore); + sf.setHostnameVerifier(MySSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); + + HttpParams params = new BasicHttpParams(); + HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); + HttpProtocolParams.setContentCharset(params, HTTP.UTF_8); + + SchemeRegistry registry = new SchemeRegistry(); + registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); + registry.register(new Scheme(OPERATION_HTTPS, sf, 443)); + registry.register(new Scheme(OPERATION_HTTPS, sf, 8443)); + registry.register(new Scheme("http", sf, 8181)); + + ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry); + return new DefaultHttpClient(ccm, params); + } catch (Exception e) { + LOG.error("Error creating HTTPs Client. Creating default client.", e); + return new DefaultHttpClient(); + } + } + + private static class MySSLSocketFactory extends SSLSocketFactory { + private SSLContext sslContext = SSLContext.getInstance("TLS"); + + private MySSLSocketFactory(KeyStore truststore) + throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException { + super(truststore); + + TrustManager tm = new X509TrustManager() { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { + LOG.debug("Inside checkClientTrusted"); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { + LOG.debug("Inside checkServerTrusted"); + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[1]; + } + }; + + sslContext.init(null, new TrustManager[] {tm}, null); + } + + @Override + public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException { + return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose); + } + + @Override + public Socket createSocket() throws IOException { + return sslContext.getSocketFactory().createSocket(); + } + } + +} diff --git a/appc-core/appc-common-bundle/java/org/onap/appc/util/JsonUtil.java b/appc-core/appc-common-bundle/java/org/onap/appc/util/JsonUtil.java new file mode 100644 index 000000000..14784595c --- /dev/null +++ b/appc-core/appc-common-bundle/java/org/onap/appc/util/JsonUtil.java @@ -0,0 +1,126 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * 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.appc.util; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.Map; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; + +public class JsonUtil { + + static ObjectMapper MAPPER = null; + static { + MAPPER = new ObjectMapper(); + MAPPER.enable(SerializationFeature.INDENT_OUTPUT); + MAPPER.configure(JsonParser.Feature.ALLOW_COMMENTS, true); + MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL); + MAPPER.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); // allow translation even + // if extra attrs exist + // in the json + MAPPER.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); + // Uncomment below when Jackson is upgraded to version 2.7 or above + // MAPPER.setPropertyNamingStrategy(PropertyNamingStrategy.KEBAB_CASE); + } + + /** + * @param valueAsString a valid json Map represented as String + * @return a flat map that each entry key derived from hierarchy path in the json object and + * flatted to a dotted separated string. e.g. + * "{\"A\":\"A-value\",\"B\":{\"C\":\"B.C-value\",\"D\":\"B.D-value\"}}"; will be + * represented as {A=A-value, B.C=B.C-value, B.D=B.D-value} when it required that the + * input will not be flatted the json string should be formatted as below example: e.g. + * "{\"A\":\"A-value\",\"B\":\"{\\\"C\\\":\\\"C-value\\\",\\\"D\\\":\\\"D-value\\\"}\"}" + * will be represented as {A=A-value, B={"C":"C-value","D":"D-value"}} + * @throws IOException when the object is not valid json Map + */ + public static Map convertJsonStringToFlatMap(String valueAsString) + throws IOException { + Map readValueMap = MAPPER.readValue(valueAsString, Map.class); + return org.onap.appc.util.ObjectMapper.map(readValueMap); + } + + /** + * 0 is the getStackTrace method 1 is the current method 2 is the parent method, 3 is the + * grandparent method or the parent class in this case. + */ + private static final int PARENT_CLASS_INDEX = 3; + + + /** + * @see #readInputJson(String, Class, Class) + */ + public static T readInputJson(String location, Class returnClass) throws IOException { + return readInputJson(location, returnClass, getCallingClass(PARENT_CLASS_INDEX)); + } + + /** + * @param location The location or name of the file we are trying to read e.g. JsonBody.json + * @param returnClass The class *this* Json is suppose to represent. + * @param locationClass The starting point for json lookup. the value specified by location is + * relative to this class. + * @return The object being returned + * @throws IOException Can't find the specified json file at Location. + */ + public static T readInputJson(String location, Class returnClass, Class locationClass) + throws IOException { + try (InputStream is = locationClass.getResourceAsStream(location)) { + validateInput(is, location); + return MAPPER.readValue(is, returnClass); + } + } + + /** + * Note that this method is sensitive to the depth of the call stack. For example if a public + * method calls a private method, that calls this method likely the desired classIndex value is + * 4 rather than 3. However, it's convenient to reduce the input required by callers of this + * class. + * + * @param classIndex How far up the stack trace to find the class we want. + * @return The class that called one of the public methods of this class. + */ + private static Class getCallingClass(int classIndex) { + String className = Thread.currentThread().getStackTrace()[classIndex].getClassName(); + try { + return Class.forName(className); + } catch (ClassNotFoundException e) { + // Theoretically impossible. + throw new IllegalStateException( + "Could not do class lookup for class in our stack trace?!?"); + } + } + + private static void validateInput(InputStream is, String location) + throws FileNotFoundException { + if (is == null) { + throw new FileNotFoundException(String.format("Could not find file at '%s'", location)); + } + } + +} diff --git a/appc-core/appc-common-bundle/java/org/onap/appc/util/MessageFormatter.java b/appc-core/appc-common-bundle/java/org/onap/appc/util/MessageFormatter.java new file mode 100644 index 000000000..a46047eaa --- /dev/null +++ b/appc-core/appc-common-bundle/java/org/onap/appc/util/MessageFormatter.java @@ -0,0 +1,90 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * 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.appc.util; + +import org.apache.commons.lang3.StringUtils; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + + +public class MessageFormatter { + private final static String paramNameRegexGroupName = "paramName"; + + /** + * start with ${ and after there is one or more characters that are not $ and not } and ended with } + */ + private final static String paramRegex = "\\$\\{(?[^}$]+)\\}"; + + public static String format(String messageTemplate, Map params) { + if (StringUtils.isEmpty(messageTemplate)) + return ""; + if (params == null || params.isEmpty()) + return messageTemplate; + + String formattedMessage = messageTemplate; + if (formattedMessage.contains("$")) { + for (Map.Entry entry : params.entrySet()) { + formattedMessage = formattedMessage.replaceAll("\\$\\{" + entry.getKey() + "\\}", + escapeDollarChar(String.valueOf(entry.getValue()))); + } + } + + return formattedMessage; + } + + private static String escapeDollarChar(String msg) { + String formatedMsg = msg; + if (formatedMsg.contains("$")) { + formatedMsg = formatedMsg.replaceAll("\\$", "\\\\\\$"); + + } + return formatedMsg; + } + + public static List getParamsNamesList(String messageTemplate) { + List paramsNames = null; + if (!StringUtils.isEmpty(messageTemplate)) { + paramsNames = new ArrayList<>(); + Matcher m = Pattern.compile(paramRegex).matcher(messageTemplate); + while (m.find()) { + String paramName = m.group(paramNameRegexGroupName); + paramsNames.add(paramName); + } + } + return paramsNames; + } + + public static Set getParamsNamesSet(String messageTemplate) { + List paramsNamesList = getParamsNamesList(messageTemplate); + Set paramsNamesSet = null; + if (paramsNamesList != null && !paramsNamesList.isEmpty()) { + paramsNamesSet = new HashSet(); + for (String paramName : paramsNamesList) { + paramsNamesSet.add(paramName); + } + } + return paramsNamesSet; + } +} diff --git a/appc-core/appc-common-bundle/java/org/onap/appc/util/ObjectMapper.java b/appc-core/appc-common-bundle/java/org/onap/appc/util/ObjectMapper.java new file mode 100644 index 000000000..e4b76dd7c --- /dev/null +++ b/appc-core/appc-common-bundle/java/org/onap/appc/util/ObjectMapper.java @@ -0,0 +1,100 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * 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.appc.util; + +import java.lang.reflect.Array; +import java.util.Map; + +public class ObjectMapper { + + private ObjectMapper() { + } + + private static void dispatch(PathContext context, Object obj) { + + if (obj == null) { + return; + } + + final Class cls = obj.getClass(); + + if (cls.isPrimitive() + || String.class.isAssignableFrom(cls) + || Number.class.isAssignableFrom(cls) + || Boolean.class.isAssignableFrom(cls)) { + handlePrimitive(context, obj); + } else if (cls.isArray()) { + handleArray(context, obj); + } else if (Map.class.isAssignableFrom(cls)) { + handleMap(context, (Map) obj); + } else if (Iterable.class.isAssignableFrom(cls)) { + handleCollection(context, Iterable.class.cast(obj)); + } else { + throw new IllegalArgumentException(obj.getClass().getName()); + } + } + + public static Map map(Object obj) { + PathContext context = new PathContext(); + dispatch(context, obj); + return context.entries(); + } + + private static void handleMap(PathContext context, Map val) { + for (Map.Entry entry : val.entrySet()) { + context.pushToken(entry.getKey().toString()); + dispatch(context, entry.getValue()); + context.popToken(); + } + } + + private static void handleCollection(PathContext context, Iterable val) { + int index = 0; + for (Object elem : val) { + handleElement(context, index++, elem); + } + } + + private static void handleArray(PathContext context, Object val) { + for (int i = 0, n = Array.getLength(val); i < n; i++) { + handleElement(context, i, Array.get(val, i)); + } + } + + private static void handleElement(PathContext context, int index, Object val) { + if (val == null) { + return; + } + + String modifier = new StringBuilder().append('[').append(Integer.valueOf(index)).append(']').toString(); + + context.pushModifier(modifier); + dispatch(context, val); + context.popModifier(); + } + + private static void handlePrimitive(PathContext context, Object val) { + context.entry(context.getPath(), val.toString()); + } +} diff --git a/appc-core/appc-common-bundle/java/org/onap/appc/util/PathContext.java b/appc-core/appc-common-bundle/java/org/onap/appc/util/PathContext.java new file mode 100644 index 000000000..d57837c14 --- /dev/null +++ b/appc-core/appc-common-bundle/java/org/onap/appc/util/PathContext.java @@ -0,0 +1,102 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * 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.appc.util; + + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.Map; + +class PathContext { + + private StringBuilder path = new StringBuilder(128); + + private LinkedList indexes = new LinkedList<>(); + private Map entries = new LinkedHashMap<>(); + private int offset = 0; + + private final String delimiter; + + PathContext() { + this("."); + } + + PathContext(String delimiter) { + this.delimiter = delimiter; + } + + private void push(String elem, boolean delimit) { + if (elem == null) { + throw new IllegalArgumentException(); + } + + int length = elem.length(); + + if (delimit && !indexes.isEmpty()) { + path.append(delimiter); + length += delimiter.length(); + } + + path.append(elem); + offset += length; + indexes.addLast(Integer.valueOf(length)); + } + + private void pop() { + if (indexes.isEmpty()) { + throw new IllegalStateException(); + } + offset -= indexes.removeLast(); + path.setLength(offset); + } + + void pushToken(String token) { + push(token, true); + } + + void popToken() { + pop(); + } + + void pushModifier(String modifier) { + push(modifier, false); + } + + void popModifier() { + pop(); + } + + String getPath() { + return path.substring(0, offset); + } + + void entry(String name, String value) { + entries.put(name, value); + } + + Map entries() { + return Collections.unmodifiableMap(entries); + } +} diff --git a/appc-core/appc-common-bundle/java/org/onap/appc/util/StreamHelper.java b/appc-core/appc-common-bundle/java/org/onap/appc/util/StreamHelper.java new file mode 100644 index 000000000..4468c841b --- /dev/null +++ b/appc-core/appc-common-bundle/java/org/onap/appc/util/StreamHelper.java @@ -0,0 +1,62 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * 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.appc.util; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; + +public class StreamHelper { + + /** + * private default constructor prevents instantiation + */ + private StreamHelper() { + } + + /** + * @param inputStream + * @return Input stream converted to string + */ + public static String getStringFromInputStream(InputStream inputStream) { + StringBuffer buffer = new StringBuffer(); + byte[] array = new byte[4096]; + + if (inputStream != null) { + try { + int len = inputStream.read(array); + while (len != -1) { + buffer.append(new String(array, 0, len, Charset.forName("UTF-8"))); + len = inputStream.read(array); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + return buffer.toString(); + } + +} diff --git a/appc-core/appc-common-bundle/java/org/onap/appc/util/StringHelper.java b/appc-core/appc-common-bundle/java/org/onap/appc/util/StringHelper.java new file mode 100644 index 000000000..920ce4228 --- /dev/null +++ b/appc-core/appc-common-bundle/java/org/onap/appc/util/StringHelper.java @@ -0,0 +1,604 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * 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.appc.util; + +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * This class contains several static helper methods that can be used to perform string manipulation algorithms. + */ + +public final class StringHelper { + + private static final EELFLogger logger = EELFManager.getInstance().getLogger(StringHelper.class); + + public static final String DASH = "-"; + public static final String DOT = "."; + public static final String ELLIPSES = "..."; + public static final String LINE_FEED = "\n"; + public static final String SLASH = "/"; + public static final String COMMA = ","; + + /** + * Converts the specified string pattern to a regular expression string. If the supplied string is null or empty, + * then a regular expression that matches all strings (.*) is returned. + *

+ * The expression passed to this method should not already be a regular expression. If it contains problematic + * meta-characters for this routine (such as period, asterisk, and plus), they will be escaped and matched literally + * in the resulting regular expression returned. + *

+ * + * @param value + * The pattern that we need to convert to a regular expression + * @return The regular expression that is equivalent to the pattern + */ + public static String convertToRegex(String value) { + if (value == null || value.trim().length() == 0) { + return ".*"; + } + + StringBuilder builder = new StringBuilder(value.trim()); + + /* + * If there are any period characters, we need to escape them so that they are exactly matched + */ + Pattern pattern = Pattern.compile("\\."); + Matcher matcher = pattern.matcher(builder); + int position = 0; + while (matcher.find(position)) { + builder.replace(matcher.start(), matcher.end(), "\\."); + position = matcher.end() + 1; + } + + /* + * If there are any asterisks or pluses, which we need to interpret as wildcard characters, we need to convert + * them into .* or . + */ + pattern = Pattern.compile("\\*|\\+"); + matcher = pattern.matcher(builder); + + /* + * If the string contains a .* meta-character sequence anywhere in the middle of the string (i.e., there are + * other characters following the .* meta-characters), OR the string ends with the .+ sequence, then we need to + * append the "end-of-line" boundary condition to the end of the string to get predictable results. + */ + if (resolveAppendingEOL(builder, matcher)) { + builder.append("$"); + } + return builder.toString(); + } + + private static boolean resolveAppendingEOL(StringBuilder builder, Matcher matcher) { + int position = 0; + boolean appendEOL = false; + + while (matcher.find(position)) { + String metachar = builder.substring(matcher.start(), matcher.end()); + if ("*".equals(metachar)) { + builder.replace(matcher.start(), matcher.end(), ".*"); + position = matcher.end() + 1; + if (matcher.end() < builder.length() - 1) { + appendEOL = true; + } + } else if ("+".equals(metachar)) { + builder.replace(matcher.start(), matcher.end(), "."); + position = matcher.end(); + if (matcher.end() == builder.length()) { + appendEOL = true; + } + } + } + return appendEOL; + } + + /** + * Takes a string that may possibly be very long and return a string that is at most maxLength. If the string is + * longer than maxLength, the last three characters will be the ellipses (...) to indicate that the string was + * shortened. + * + * @param possiblyLongString + * @param maxLength + * must be at least 4 (one character plus ellipses) + * @return possibly shortened string + */ + public static String getShortenedString(String possiblyLongString, int maxLength) { + if ((possiblyLongString != null) && (maxLength > ELLIPSES.length()) + && (possiblyLongString.length() > maxLength)) { + return possiblyLongString.substring(0, maxLength - ELLIPSES.length()) + ELLIPSES; + + } + return possiblyLongString; + } + + /** + * Determines that a provided string is not null and not empty (length = 0 after trimming) + * + * @param theString + * The string to be tested + * @return true if the string IS NOT null and is NOT empty + */ + public static boolean isNotNullNotEmpty(String theString) { + return theString != null && !theString.trim().isEmpty(); + } + + /** + * Determines that a provided string IS null or an empty string (length = 0 after trimming) + * + * @param theString + * The string to be tested + * @return true if the string IS null OR is empty + */ + public static boolean isNullOrEmpty(String theString) { + return theString == null || theString.trim().isEmpty(); + } + + /** + * Returns an indication if the first string is equal to the second string, allowing for either or both strings to + * be null. + * + * @param a + * The first string to be compared + * @param b + * The second string to be compared + * @return True if both strings are null, or both strings are non-null AND they are equal. False otherwise. + */ + public static boolean areEqual(String a, String b) { + return areEqual(a, b, false); + } + + /** + * Returns an indication if the first string is equal to the second string, allowing for either or both strings to + * be null, and ignoring case. + * + * @param a + * The first string to be compared + * @param b + * The second string to be compared + * @return True if both strings are null, or both strings are non-null AND they are equal (without regard to case). + * False otherwise. + */ + public static boolean equalsIgnoreCase(String a, String b) { + return areEqual(a, b, true); + } + + /** + * Compares two strings (allowing either or both to be null), and allowing for optional case sensitive or + * insensitive comparison. + * + * @param a + * The first string to be compared + * @param b + * The second string to be compared + * @param caseInsensitive + * True if the comparison is to be case in-sensitive. + * @return True if both strings are null, or both strings are non-null and they are equal + */ + private static boolean areEqual(String a, String b, boolean caseInsensitive) { + if (a == null && b == null) { + return true; + } + if (a != null && b != null) { + if (caseInsensitive) { + return a.equalsIgnoreCase(b); + } else { + return a.equals(b); + } + } + + return false; + } + + /** + * This method is used to mangle a name. + *

+ * This method will first remove all unacceptable characters from the name and translate all characters to lower + * case. This is done to eliminate any potentially troublesome characters. If the resulting string is empty, then a + * random string of characters for the minimum desired length is returned. If the string is too short to meet the + * minimum length requirement, it is padded with random characters. + *

+ *

+ * Once the string has been scrubbed and possibly padded, it may be truncated (if longer than the maximum value) and + * the result is returned. To make the string as unique as possible, the algorithm removes excess letters from the + * center of the string, concatenating the first nad last parts of the name together. The assumption is that users + * tend to start the names of multiple things in similar ways, and get more descriptive as the name progresses. If + * for example, several objects were named "A test Object", "A test Object1", and "A test Object2", shortening the + * name only from the left does not generate a unique name. + *

+ * + * @param name + * The name to be mangled + * @param minLen + * minimum number of characters for the name + * @param maxLen + * maximum number of characters for the name + * @return The mangled name, or an empty string if the value is null or an empty string. + */ + public static String mangleName(String name, int minLen, int maxLen) { + StringBuilder builder = new StringBuilder(name == null ? "" : name); + Pattern pattern = Pattern.compile("[^a-z0-9]+", Pattern.CASE_INSENSITIVE); + Matcher matcher = pattern.matcher(builder); + int position = 0; + while (matcher.find(position)) { + builder.delete(matcher.start(), matcher.end()); + position = matcher.start(); + } + + if (builder.length() < minLen) { + for (int i = builder.length(); i <= minLen; i++) { + builder.append("A"); + } + } + + /* + * Remove out of the center of the name to preserve start and end and result in a string of max len + */ + if (builder.length() > maxLen) { + int excess = builder.length() - maxLen; + int left = maxLen / 2; + + builder.delete(left, excess + left); + } + + return builder.toString().toLowerCase(); + } + + /** + * This method is used to normalize a string value. + *

+ * This method will ensure that the string value is trimmed of all leading and trailing whitespace if not null. If + * it is null or an empty string, then it will return null. + *

+ * + * @param value + * The value to be normalized + * @return The normalized (no leading or trailing whitespace) value, or null if the string was null or an empty + * string (or all whitespace). This method will never return an empty string. + */ + public static String normalizeString(String value) { + if (value != null) { + String temp = value.trim(); + if (temp.length() > 0) { + return temp; + } + } + return null; + } + + /** + * This method is used to strip all carriage returns and line feed characters from a string + * + * @param value + * @return The original value less all carriage returns and line feeds + */ + public static String stripCRLF(String value) { + + if (value == null) { + return null; + } + String[] tokens = value.split("\r\n|\n\r|\r|\n"); + StringBuilder builder = new StringBuilder(); + for (String token : tokens) { + builder.append(token.trim()); + } + return builder.toString(); + } + + /** + * Converts UNIX-style line endings to DOS-style. Replaces LF with CR+LF as long as the LF does not already exist + * paired with a CR. + * + * @param content + * The content to be converted + * @return The converted content. + */ + public static String toDOSLines(String content) { + if (content == null) { + return null; + } + + StringBuilder builder = new StringBuilder(content); + Pattern pattern = Pattern.compile("^(\n)[^\r]|[^\r](\n)[^\r]|[^\r](\n)$"); + Matcher matcher = pattern.matcher(builder); + int position = 0; + while (matcher.find(position)) { + int index = matcher.start(1); + if (index == -1) { + index = matcher.start(2); + } + if (index == -1) { + index = matcher.start(3); + } + + builder.replace(index, index + 1, "\r\n"); + position = index + 1; + } + + return builder.toString(); + } + + /** + * This method will convert a string contents to use the UNIX-style line endings. That is, all occurrences of CR + * (Carriage Return) and LF (Line Feed) are reduced to just use LF. + * + * @param content + * The buffer to be processed + * @return The converted contents + */ + public static String toUnixLines(String content) { + if (content == null) { + return null; + } + + StringBuilder builder = new StringBuilder(content); + Pattern pattern = Pattern.compile("\r\n|\n\r"); + Matcher matcher = pattern.matcher(builder); + int position = 0; + while (matcher.find(position)) { + builder.replace(matcher.start(), matcher.end(), "\n"); + position = matcher.start(); + } + + return builder.toString(); + } + + /** + * This method is used to translate characters in the input sequence that match the characters in the match list to + * the corresponding character in the replacement list. If the replacement list is shorter than the match list, then + * the character from the replacement list is taken as the modulo of the match character position and the length of + * the replacement list. + * + * @param sequence + * The input sequence to be processed + * @param match + * The list of matching characters to be searched + * @param replacement + * The list of replacement characters, positional coincident with the match list. If shorter than the + * match list, then the position "wraps" around on the replacement list. + * @return The translated string contents. + */ + public static Object translate(String sequence, String match, String replacement) { + + if (sequence == null) { + return null; + } + + StringBuilder builder = new StringBuilder(sequence); + + for (int index = 0; index < builder.length(); index++) { + char ch = builder.charAt(index); + + int position = match.indexOf(ch); + if (position == -1) { + continue; + } + + if (position >= replacement.length()) { + position %= replacement.length(); + } + builder.setCharAt(index, replacement.charAt(position)); + } + + return builder.toString(); + } + + /** + * Ensures that the name provided is a valid identifier. This means that no spaces are allowed as well as special + * characters. This method translates all spaces and illegal characters to underscores (_). + * + * @param name + * The name to be checked and converted to an identifier if needed + * @return The valid identifier from the name + */ + public static String validIdentifier(String name) { + if (name == null || name.length() == 0) { + return name; + } + StringBuilder builder = new StringBuilder(name); + for (int index = 0; index < builder.length(); index++) { + char ch = builder.charAt(index); + + if ((index == 0 && !Character.isJavaIdentifierStart(ch)) || (!Character.isJavaIdentifierPart(ch))) { + builder.setCharAt(index, '_'); + } + } + return builder.toString(); + } + + + /** + * Private constructor to prevent instantiation of this class - All methods are static! + */ + private StringHelper() { + + } + + /** + * This method verifies that the provided string only contains characters from the legal set, and replaces any + * character not in the legal set with the specified replacement character. + * + * @param sequence + * The sequence to be verified + * @param legal + * The set of all legal characters + * @param replacement + * The replacement character if a character is not in the legal set + * @return The verified *and possibly updated) string + */ + public static String verify(String sequence, String legal, char replacement) { + if (sequence == null) { + return null; + } + + StringBuilder builder = new StringBuilder(sequence); + for (int index = 0; index < builder.length(); index++) { + char ch = builder.charAt(index); + if (legal.indexOf(ch) == -1) { + builder.setCharAt(index, replacement); + } + } + return builder.toString(); + } + + /** + * @param list + * The list of elements + * @return The list of elements formatted as a comma-delimited list + */ + public static String asList(List list) { + StringBuilder builder = new StringBuilder(); + + if (list != null) { + if (list.size() == 1) { + builder.append(list.get(0)); + } else { + for (String element : list) { + builder.append(element); + builder.append(", "); + } + + if (builder.length() > 2) { + builder.delete(builder.length() - 2, builder.length()); + } + } + } + return builder.toString(); + } + + /** + * @param map + * A map of strings + * @return A map expressed as a comma-delimited list of name=value tuples + */ + public static String asList(Map map) { + StringBuilder builder = new StringBuilder(); + if (map != null) { + Set keys = map.keySet(); + for (String key : keys) { + builder.append(String.format("%s=%s, ", key, map.get(key))); + } + + if (builder.length() > 2) { + builder.delete(builder.length() - 2, builder.length()); + } + } + return builder.toString(); + } + + /** + * @param values + * An array or varargs of Strings to be concatenated into a comma-separated list + * @return The comma-seprated list of values + */ + public static String asList(String... values) { + StringBuilder builder = new StringBuilder(); + builder.append('['); + if (values != null && values.length > 0) { + int count = values.length; + for (int index = 0; index < count - 1; index++) { + builder.append(values[index]); + builder.append(','); + } + builder.append(values[count - 1]); + } + builder.append(']'); + return builder.toString(); + } + + public static Object resolveToType(String input) { + String intRegex = "^(\\-)?[0-9]+$"; + String doubleRegex = "^(\\-)?[0-9\\.]+$"; + String boolRegex = "(^(?i)((true)|(false))$)"; + + // Check for null + if (input == null) { + return null; + } + + // Check int first + if (input.matches(intRegex)) { + try { + return Integer.parseInt(input); + } catch (NumberFormatException nfe) { + // Should not happen + logger.error(nfe.getMessage()); + } + } + + // Check double (int + decimal point) + if (input.matches(doubleRegex)) { + try { + return Double.parseDouble(input); + } catch (NumberFormatException | NullPointerException e) { + // NPE won't happen bc of regex check + logger.error("Parsing input failed", e); + } + } + + // Check boolean + if (input.matches(boolRegex)) { + return Boolean.parseBoolean(input); + } + + // Try to parse a date + Date date = Time.utcParse(input); + if (date != null) { + return date; + } + + // No special type, return string + return input; + } + + /** + * Converts a properties object to a string in the format of
[ key=value, key=value, ... ]
+ * + * @param props + * The properties object to format + * @return A string in the format
[ key=value, ... ]
or null if the input was null + */ + public static String propertiesToString(Properties props) { + if (props == null) { + return null; + } + StringBuilder out = new StringBuilder(); + out.append("["); + for (Object key : props.keySet()) { + out.append(String.format(" %s = %s,", key.toString(), props.getProperty(key.toString()))); + } + if (props.size() > 0) { + out.deleteCharAt(out.lastIndexOf(",")); + } + out.append(" ]"); + return out.toString(); + } +} diff --git a/appc-core/appc-common-bundle/java/org/onap/appc/util/StructuredPropertyHelper.java b/appc-core/appc-common-bundle/java/org/onap/appc/util/StructuredPropertyHelper.java new file mode 100644 index 000000000..5c18f3a1c --- /dev/null +++ b/appc-core/appc-common-bundle/java/org/onap/appc/util/StructuredPropertyHelper.java @@ -0,0 +1,259 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * 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.appc.util; + +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * This class is used to assemble properties that are defined using a structured name into groups, and allow them to be + * processed as sets of definitions. + *

+ * For example, a structured name uses a dotted-notation, like "provider.name". Further, the nodes of the structured + * name may be serialized using a suffix ordinal number (e.g., "provider1.name"). These structured properties form a + * hierarchical name space where the names are grouped together and can be retrieved as a set. + *

+ * + */ + +public class StructuredPropertyHelper { + + /** + * This method scans the properties object for all properties that match the root name and constructs a list of + * structured property node graphs that represents the namespaces of the properties. + *

+ * For example, assume that there are structured properties of the form "provider1.name", "provider2.name", + * "provider3.name", and so forth. There may also be other subordinate properties as well (e.g., "provider1.type"). + * This method would construct a list of graphs of nodes, where each node represents one value of the structured + * name. The roots would be the values "provider1", "provider2", "provider3", and so forth. The values of the + * subordinate nodes would be the second, third, and so forth name nodes of the compound name. The value of the + * property is associated with nodes that are representative of the leaf of the name space. + *

+ * + * @param properties + * The properties to be processed + * @param prefix + * The prefix of the root structured property name + * @return The node graph of the properties + */ + public static List getStructuredProperties(Properties properties, String prefix) { + List roots = new ArrayList<>(); + + for (String name : properties.stringPropertyNames()) { + if (name.startsWith(prefix)) { + String value = properties.getProperty(name); + processNamespace(roots, name, value); + } + } + + return roots; + } + + /** + * This method recursively walks the name space of the structured property and constructs the node graph to + * represent the property + * + * @param nodes + * The collection of nodes for the current level of the name space + * @param propertyName + * The name of the node + * @param value + * The value, if any + * @return The node for this level in the namespace + */ + @SuppressWarnings("nls") + private static Node processNamespace(List nodes, String propertyName, String value) { + String[] tokens = propertyName.split("\\.", 2); + String nodeName = normalizeNodeName(tokens[0]); + + Node namespaceNode = null; + for (Node node : nodes) { + if (node.getName().equals(nodeName)) { + namespaceNode = node; + break; + } + } + if (namespaceNode == null) { + namespaceNode = new Node(); + namespaceNode.setName(nodeName); + nodes.add(namespaceNode); + } + + if (tokens.length == 1 || tokens[1] == null || tokens[1].length() == 0) { + namespaceNode.setValue(value); + } else { + processNamespace(namespaceNode.getChildren(), tokens[1], value); + } + + return namespaceNode; + } + + /** + * This method normalizes a node name of the structured property name by removing leading and trailing whitespace, + * and by converting any ordinal position to a simple expression without leading zeroes. + * + * @param token + * The token to be normalized + * @return The normalized name, or null if the token was null; + */ + @SuppressWarnings("nls") + private static String normalizeNodeName(String token) { + if (token == null) { + return null; + } + + StringBuffer buffer = new StringBuffer(token.trim()); + Pattern pattern = Pattern.compile("([^0-9]+)([0-9]*)"); + Matcher matcher = pattern.matcher(buffer); + if (matcher.matches()) { + String nameRoot = matcher.group(1); + String ordinal = matcher.group(2); + if (ordinal != null && ordinal.length() > 0) { + int i = Integer.parseInt(ordinal); + buffer.setLength(0); + buffer.append(nameRoot); + buffer.append(Integer.toString(i)); + } + } + return buffer.toString(); + } + + /** + * This class represents a node in the structured property name space + * + */ + public static class Node implements Comparable { + + /** + * The name of the structured property node + */ + private String name; + + /** + * If the node is a leaf, then the value of the property + */ + private String value; + + /** + * If the node is not a leaf, then the sub-nodes of the property + */ + private List children; + + /** + * @return the value of name + */ + public String getName() { + return name; + } + + /** + * @param name + * the value for name + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return the value of value + */ + public String getValue() { + return value; + } + + /** + * @param value + * the value for value + */ + public void setValue(String value) { + this.value = value; + } + + /** + * @return the value of children + */ + public List getChildren() { + if (children == null) { + children = new ArrayList<>(); + } + return children; + } + + /** + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return name.hashCode() + (value != null ? value.hashCode() : children.hashCode()); + } + + /** + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (obj == null) + return false; + if (this.getClass() != obj.getClass()) + return false; + + Node other = (Node) obj; + boolean result = name.equals(other.name); + + if (value == null) { + result &= other.value == null; + } else { + result &= value.equals(other.value); + } + if (children == null) { + result &= other.children == null; + } else { + result &= children.equals(other.children); + } + return result; + } + + /** + * @see java.lang.Object#toString() + */ + @SuppressWarnings("nls") + @Override + public String toString() { + if (value != null) { + return String.format("%s = %s", name, value); + } + return String.format("%s.%s", name, children.toString()); + } + + @Override + public int compareTo(StructuredPropertyHelper.Node o) { + return name.compareTo(o.name); + } + } +} diff --git a/appc-core/appc-common-bundle/java/org/onap/appc/util/Time.java b/appc-core/appc-common-bundle/java/org/onap/appc/util/Time.java new file mode 100644 index 000000000..dfd1ee769 --- /dev/null +++ b/appc-core/appc-common-bundle/java/org/onap/appc/util/Time.java @@ -0,0 +1,601 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ================================================================================ + * Modifications Copyright (c) 2019 IBM + * ================================================================================ + * 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.appc.util; + +import java.sql.Timestamp; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.List; +import java.util.Locale; +import java.util.SimpleTimeZone; +import java.util.TimeZone; +import javax.xml.datatype.DatatypeConfigurationException; +import javax.xml.datatype.DatatypeFactory; +import javax.xml.datatype.XMLGregorianCalendar; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class is a general purpose helper class to augment standard Java time support. + * + */ + +public final class Time { + + /** + * Logger to log operations + */ + private static final Logger LOG = LoggerFactory.getLogger(Time.class); + + /** + * A formatter to be used to format values + */ + private static SimpleDateFormat dateformatter = null; + + /** + * The UTC timezone (for UTC or GMT time) + */ + @SuppressWarnings("nls") + private static final TimeZone utcTZ = TimeZone.getTimeZone("UTC"); + + /** + * The cached reference to the datatype factory + */ + private static DatatypeFactory xmlDatatypeFactory = null; + + /** + * Private default constructor prevents instantiation + */ + private Time() { + // + } + + /** + * Increments a date by the indicated months, days, hours, minutes, and seconds, and returns the + * updated date. + * + * @param date The date to be manipulated + * @param months The number of months to be added to the date + * @param days The number of days to be added to the date + * @param hours The number of hours to be added to the date + * @param minutes The number of minutes to be added to the date + * @param seconds The number of seconds to be added to the date + * @return The updated date. + */ + public static Date addTime(final Date date, final int months, final int days, final int hours, + final int minutes, final int seconds) { + Calendar cal = Calendar.getInstance(); + cal.setTime(date); + cal.add(Calendar.MONTH, months); + cal.add(Calendar.DATE, days); + cal.add(Calendar.HOUR_OF_DAY, hours); + cal.add(Calendar.MINUTE, minutes); + cal.add(Calendar.SECOND, seconds); + return cal.getTime(); + } + + /** + * Clears the time components of a calendar to zero, leaving the date components unchanged. + * + * @param cal the calendar to be updated + * @return The updated calendar object + */ + public static Calendar dateOnly(final Calendar cal) { + cal.set(Calendar.HOUR_OF_DAY, 0); + cal.set(Calendar.MINUTE, 0); + cal.set(Calendar.SECOND, 0); + cal.set(Calendar.MILLISECOND, 0); + return cal; + } + + /** + * This method returns the local time that corresponds to the end of the current day + * + * @return The time that corresponds to the end of the current day, expressed as local time + */ + public static Date endOfDayLocal() { + return endOfDayLocal(new Date()); + } + + /** + * This method returns the last moment of the day for the supplied local time. This is defined + * as the millisecond before midnight of the current date represented by the local time. + * + * @param localTime The local time for which the last moment of the day is desired. + * @return The millisecond prior to midnight, local time. + */ + public static Date endOfDayLocal(final Date localTime) { + // @sonar:off + GregorianCalendar calendar = new GregorianCalendar(); + calendar.setTime(localTime); + calendar.set(Calendar.HOUR, 11); + calendar.set(Calendar.AM_PM, Calendar.PM); + calendar.set(Calendar.MINUTE, 59); + calendar.set(Calendar.SECOND, 59); + calendar.set(Calendar.MILLISECOND, 999); + // @sonar:on + + return calendar.getTime(); + } + + /** + * The end of the current day and in the current time zone expressed as a UTC time. + * + * @return The UTC time that corresponds to the end of the current day + */ + public static Date endOfDayUTC() { + return endOfDayUTC(new Date()); + } + + /** + * Returns the UTC time that corresponds to the end of the day for the local time specified, + * using the current (default) time zone. + * + * @param localTime The local time for which we are requesting the UTC time that corresponds to + * the end of the day + * @return The UTC time that corresponds to the end of the local day specified by the local + * time. + */ + public static Date endOfDayUTC(final Date localTime) { + return endOfDayUTC(localTime, TimeZone.getDefault()); + } + + /** + * Returns the time expressed in UTC time of the end of the day specified in local time and + * within the local time zone. + * + * @param localTime The local time for which we will compute the end of the local day, and then + * convert to UTC time. + * @param localTimeZone The time zone that the local time is within. + * @return The UTC date that corresponds to the end of the day local time and in the local time + * zone. + */ + public static Date endOfDayUTC(final Date localTime, final TimeZone localTimeZone) { + Date endOfDay = endOfDayLocal(localTime); + return utcDate(endOfDay, localTimeZone); + } + + /** + * returns current Date in 'UTC' Timezone + * + * @return The current date, expressed in the UTC timezone. + */ + @SuppressWarnings("nls") + public static Date getCurrentUTCDate() { + + // This code incorrectly changes the default timezone for the entire JVM in order to compute + // the UTC + // date for the current time. + + GregorianCalendar calendar = new GregorianCalendar(); + calendar.setTimeZone(TimeZone.getTimeZone("UTC")); + calendar.setTimeInMillis(utcTime()); + return calendar.getTime(); + } + + /** + * This method loads and caches the reference to the XML data type factory object. + * + * @return The XML Data Type Factory object + */ + public static DatatypeFactory getDatatypeFactory() { + if (xmlDatatypeFactory == null) { + try { + xmlDatatypeFactory = DatatypeFactory.newInstance(); + } catch (DatatypeConfigurationException e) { + LOG.error("Error while getting DatatypeFactory ::", e); + } + } + return xmlDatatypeFactory; + } + + /** + * Gives the date-time String based on given Locale and Timezone + * + * @param date The date to be formatted + * @param locale The locale that we want to format the value for + * @param timezone The time zone that the date is within + * @return The formatted value + */ + public static String getDateByLocaleAndTimeZone(final Date date, final Locale locale, + final TimeZone timezone) { + String strDate = null; + DateFormat df = + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM, locale); + df.setTimeZone(timezone); + synchronized (df) { + strDate = df.format(date); + } + return strDate; + } + + /** + * Returns singleton UTC date formatter. + * + * @return + */ + @SuppressWarnings("nls") + private static SimpleDateFormat getDateFormatter() { + if (dateformatter == null) { + dateformatter = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss"); + dateformatter.setTimeZone(new SimpleTimeZone(SimpleTimeZone.UTC_TIME, "UTC")); + } + return dateformatter; + } + + /** + * This method returns the local time that corresponds to a given UTC time in the current time + * zone. + * + * @param utcTime The UTC time for which we desire the equivalent local time in the current time + * zone. + * @return The local time that is equivalent to the given UTC time for the current time zone + */ + public static long localTime(final long utcTime) { + return localTime(utcTime, TimeZone.getDefault()); + } + + /** + * This method can be used to get the local time that corresponds to a specific UTC time. + *

+ * This method has a problem since the offset can only be determined by having a local time. So, + * we take the UTC time and add the raw offset to it to come up with an approximation of the + * local time. This gives us a local time that we can use to determine what the offset should + * be, which is what we actually add to the UTC time to get the local time. + *

+ * + * @param utcTime The UTC time for which we want to obtain the equivalent local time + * @param localTZ The time zone that we want the local time to be within + * @return The local time for the specified time zone and the given UTC time + */ + public static long localTime(final long utcTime, final TimeZone localTZ) { + int offset = localTZ.getOffset(utcTime + localTZ.getRawOffset()); + long result = utcTime + offset; + + return result; + } + + /** + * Sets the date components of a calendar to the specified values, leaving the time components + * unchanged. + * + * @param cal The calendar to be updated + * @param year The year to be set + * @param month The month to be set + * @param day The day to be set + * @return The updated calendar object + */ + public static Calendar setDate(final Calendar cal, final int year, final int month, + final int day) { + cal.set(Calendar.YEAR, year); + cal.set(Calendar.MONTH, month); + cal.set(Calendar.DAY_OF_MONTH, day); + return cal; + } + + /** + * Returns the start of the day expressed in local time for the current local time. + * + * @return The start of the day + */ + public static Date startOfDayLocal() { + return startOfDayLocal(new Date()); + } + + /** + * This method returns the date that corresponds to the start of the day local time. The date + * returned represents midnight of the previous day represented in local time. If the UTC time + * is desired, use the methods {@link #startOfDayUTC(Date, TimeZone)}, + * {@link #startOfDayUTC(Date)}, or {@link #startOfDayUTC()} + * + * @param localTime The local date that we wish to compute the start of day for. + * @return The date that corresponds to the start of the local day + */ + public static Date startOfDayLocal(final Date localTime) { + GregorianCalendar calendar = new GregorianCalendar(); + calendar.setTime(localTime); + calendar.set(Calendar.HOUR, 0); + calendar.set(Calendar.AM_PM, Calendar.AM); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + + return calendar.getTime(); + } + + /** + * This method returns the UTC date that corresponds to the start of the local day based on the + * current time and the default time zone (the time zone we are running in). + * + * @return The start of the local day expressed as a UTC time. + */ + public static Date startOfDayUTC() { + return startOfDayUTC(new Date()); + } + + /** + * This method returns the UTC date that corresponds to the start of the local day specified in + * the current time zone. + * + * @param localTime The local time to be used to compute the start of the day + * @return The start of the local day expressed as a UTC time. + */ + public static Date startOfDayUTC(final Date localTime) { + return startOfDayUTC(localTime, TimeZone.getDefault()); + } + + /** + * This method returns the UTC date that corresponds to the start of the local day specified in + * the local timezone. + * + * @param localTime The local time to be used to compute start of day + * @param localTimeZone The time zone that the local time was recorded within + * @return The corresponding UTC date + */ + public static Date startOfDayUTC(final Date localTime, final TimeZone localTimeZone) { + Date startOfDay = startOfDayLocal(localTime); + return utcDate(startOfDay, localTimeZone); + } + + /** + * This method creates and returns an XML timestamp expressed as the current UTC value for the + * system. The caller does not specify the time value or time zone using this method. This + * ensures that the timestamp value is always expressed as UTC time. + * + * @return The XMLGregorianCalendar that can be used to record the timestamp + */ + + public static XMLGregorianCalendar timestamp() { + getDatatypeFactory(); + XMLGregorianCalendar ts = xmlDatatypeFactory.newXMLGregorianCalendar(); + GregorianCalendar utc = new GregorianCalendar(); + utc.setTime(utcDate()); + ts.setTimezone(0); + ts.setYear(utc.get(Calendar.YEAR)); + // Calendar Months are from 0-11 need to +1 + ts.setMonth(utc.get(Calendar.MONTH) + 1); + ts.setDay(utc.get(Calendar.DAY_OF_MONTH)); + ts.setHour(utc.get(Calendar.HOUR_OF_DAY)); + ts.setMinute(utc.get(Calendar.MINUTE)); + ts.setSecond(utc.get(Calendar.SECOND)); + ts.setMillisecond(utc.get(Calendar.MILLISECOND)); + return ts; + } + + /** + * Converts XMLGregorianCalendar to java.util.Date in Java + * + * @param calendar the calendar object to be converted + * @return The equivalent Date object + */ + public static Date toDate(final XMLGregorianCalendar calendar) { + if (calendar == null) { + return null; + } + return calendar.toGregorianCalendar().getTime(); + } + + /** + * Converts java Date to XMLGregorianCalendar. + * + * @param date The date to convert + * @return The XMLGregorianCalendar for the specified date + */ + @SuppressWarnings("nls") + public static XMLGregorianCalendar toXMLCalendar(final Date date) { + GregorianCalendar cal = (GregorianCalendar) Calendar.getInstance(); + cal.setTime(date); + + XMLGregorianCalendar xmlCal = null; + try { + xmlCal = DatatypeFactory.newInstance().newXMLGregorianCalendar(cal); + } catch (DatatypeConfigurationException e) { + LOG.error("toXMLCalendar", e); + } + return xmlCal; + } + + /** + * Truncates the provided date so that only the date, hours, and minutes portions are + * significant. This method returns the date with the seconds and milliseconds forced to zero. + * + * @param date The date to truncate + * @return The date with only the year, month, day, hours, and minutes significant. + */ + public static Date truncDate(final Date date) { + Calendar cal = Calendar.getInstance(); + cal.setTime(date); + cal.set(Calendar.SECOND, 0); + cal.set(Calendar.MILLISECOND, 0); + return cal.getTime(); + } + + /** + * The UTC date that corresponds to the current date in the local time zone. + * + * @return The UTC date for now in the current time zone. + */ + public static Date utcDate() { + return new Date(); + } + + /** + * The UTC date for the specified date in the current (default) time zone. + * + * @param date The local date for which the UTC date is desired. + * @return The UTC date that corresponds to the date in the current time zone. + */ + public static Date utcDate(final Date date) { + TimeZone tz = TimeZone.getDefault(); + return utcDate(date, tz); + } + + /** + * Returns the UTC date for the specified date in the specified time zone. + * + * @param date The date for which the UTC date is desired in the specified zone + * @param tz The time zone that corresponds to the date to be converted to UTC + * @return The UTC date that corresponds to the local date in the local time zone. + */ + public static Date utcDate(final Date date, final TimeZone tz) { + return new Date(utcTime(date.getTime(), tz)); + } + + /** + * Format incoming date as string in GMT or UTC. + * + * @param dt The date to be formatted + * @return The date formatted for UTC timezone + */ + public static String utcFormat(final Date dt) { + String strDate = null; + DateFormat df = getDateFormatter(); + synchronized (df) { + strDate = df.format(dt); + } + return strDate; + } + + /** + * Parse previously formated Date object back to a Date object. + * + * @param dateStr The representation of a UTC date as a string + * @return The date object containing the parsed representation, or null if the representation + * cannot be parsed + */ + @SuppressWarnings("nls") + public static Date utcParse(final String dateStr) { + String[] adtl = {"yyyy-MM-dd"}; + return utcParse(dateStr, adtl); + } + + /** + * Parse previously formated Date object back to a Date object. + * + * @param dateStr The representation of a UTC date as a string + * @param adtlFormatStrings A list of strings that represent additional date format + * representations to try and parse. + * @return The date object containing the parsed representation, or null if the representation + * cannot be parsed + */ + @SuppressWarnings("nls") + public static Date utcParse(final String dateStr, String... adtlFormatStrings) { + if (dateStr != null) { + // Build the list of formatters starting with the default defined in the class + List formats = new ArrayList<>(); + formats.add(getDateFormatter()); + + if (adtlFormatStrings != null) { + for (String s : adtlFormatStrings) { + formats.add(new SimpleDateFormat(s)); + } + } + + // Return the first matching date formatter's result + for (DateFormat df : formats) { + df.setTimeZone(utcTZ); + try { + return df.parse(dateStr); + } catch (ParseException e) { + LOG.debug(String.format("IGNORE - Date string [%s] does not fit pattern [%s]", + dateStr, df.toString())); + } + } + } + return null; + } + + /** + * This method returns the current time for the UTC timezone + * + * @return The time in the UTC time zone that corresponds to the current local time. + */ + public static long utcTime() { + return new Date().getTime(); + } + + /** + * Get the UTC time that corresponds to the given time in the default time zone (current time + * zone for the system). + * + * @param localTime The time in the current time zone for which the UTC time is desired. + * @return The UTC time + */ + public static long utcTime(final long localTime) { + TimeZone tz = TimeZone.getDefault(); + return utcTime(localTime, tz); + } + + /** + * Get the UTC time that corresponds to the given time in the specified timezone. + *

+ * Note that the java getOffset() method works a little counter-intuitive. It + * returns the offset that would be added to the current UTC time to get the LOCAL time + * represented by the local time zone. That means to get the UTC time, we need to SUBTRACT this + * offset from the local time. + *

+ * + * @param localTime The time in the specified time zone for which the UTC time is desired. + * @param localTZ The time zone which the local time is in. + * @return The UTC time for the specified local time in the specified local time zone. + */ + public static long utcTime(final long localTime, final TimeZone localTZ) { + int offset = localTZ.getOffset(localTime); + return localTime - offset; + + } + + /** + * Creates a timestamp value from a time + * + * @param utcTime The UTC time to convert to a timestamp + * @return The timestamp + */ + public static Timestamp utcTimestamp(final long utcTime) { + TimeZone tz = TimeZone.getDefault(); + return new Timestamp(utcTime(utcTime, tz)); + } + + public static String dateToStringConverterMillis(Date date) { + SimpleDateFormat customDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS"); + if (date != null) { + return customDate.format(date); + } + return null; + } + + public static Date stringToDateConverterMillis(String dateString) throws ParseException { + SimpleDateFormat customDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS"); + return customDate.parse(dateString); + } +} diff --git a/appc-core/appc-common-bundle/java/org/onap/appc/util/UnmodifiableProperties.java b/appc-core/appc-common-bundle/java/org/onap/appc/util/UnmodifiableProperties.java new file mode 100644 index 000000000..6d20b64a4 --- /dev/null +++ b/appc-core/appc-common-bundle/java/org/onap/appc/util/UnmodifiableProperties.java @@ -0,0 +1,355 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * 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.appc.util; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.Reader; +import java.io.Writer; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.InvalidPropertiesFormatException; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +/** + * This utility class is used to wrap a properties object and to delegate all read operations to the property object, + * while disallowing any write or modification to the property object. + * + */ +public class UnmodifiableProperties extends Properties implements Cloneable { + + /** + * Serial number + */ + private static final long serialVersionUID = 1L; + + private static final String PROPERTY_CANNOT_BE_MODIFIED_MSG = "Property cannot be modified!"; + + /** + * The properties object which we are wrapping + */ + private Properties properties; + + /** + * Create the unmodifiable wrapper around the provided properties object + * + * @param properties + * The properties to be wrapped and protected from modification + */ + public UnmodifiableProperties(Properties properties) { + this.properties = properties; + } + + /** + * @see java.util.Hashtable#clear() + */ + @Override + public synchronized void clear() { + throw new UnsupportedOperationException(PROPERTY_CANNOT_BE_MODIFIED_MSG); + } + + /** + * @see java.util.Hashtable#clone() + */ + // @sonar:off + @Override + public synchronized Object clone() { + throw new UnsupportedOperationException(PROPERTY_CANNOT_BE_MODIFIED_MSG); + } + + // @sonar:on + + /** + * @see java.util.Hashtable#contains(java.lang.Object) + */ + @Override + public synchronized boolean contains(Object value) { + return properties.contains(value); + } + + /** + * @see java.util.Hashtable#containsKey(java.lang.Object) + */ + @Override + public synchronized boolean containsKey(Object key) { + return properties.containsKey(key); + } + + /** + * @see java.util.Hashtable#containsValue(java.lang.Object) + */ + @Override + public boolean containsValue(Object value) { + return properties.containsValue(value); + } + + /** + * @see java.util.Hashtable#elements() + */ + @Override + public synchronized Enumeration elements() { + return properties.elements(); + } + + /** + * @see java.util.Hashtable#entrySet() + */ + @Override + public Set> entrySet() { + return Collections.unmodifiableSet(properties.entrySet()); + } + + /** + * @see java.util.Hashtable#equals(java.lang.Object) + */ + @Override + public synchronized boolean equals(Object o) { + return properties.equals(o); + } + + /** + * @see java.util.Hashtable#get(java.lang.Object) + */ + @Override + public synchronized Object get(Object key) { + return properties.get(key); + } + + /** + * @see java.util.Properties#getProperty(java.lang.String) + */ + @Override + public String getProperty(String key) { + return properties.getProperty(key); + } + + /** + * @see java.util.Properties#getProperty(java.lang.String, java.lang.String) + */ + @Override + public String getProperty(String key, String defaultValue) { + return properties.getProperty(key, defaultValue); + } + + /** + * @see java.util.Hashtable#hashCode() + */ + @Override + public synchronized int hashCode() { + return properties.hashCode(); + } + + /** + * @see java.util.Hashtable#isEmpty() + */ + @Override + public synchronized boolean isEmpty() { + return properties.isEmpty(); + } + + /** + * @see java.util.Hashtable#keys() + */ + @Override + public synchronized Enumeration keys() { + return properties.keys(); + } + + /** + * @see java.util.Hashtable#keySet() + */ + @Override + public Set keySet() { + return Collections.unmodifiableSet(properties.keySet()); + } + + /** + * @see java.util.Properties#list(java.io.PrintStream) + */ + @Override + public void list(PrintStream out) { + properties.list(out); + } + + /** + * @see java.util.Properties#list(java.io.PrintWriter) + */ + @Override + public void list(PrintWriter out) { + properties.list(out); + } + + /** + * @see java.util.Properties#load(java.io.InputStream) + */ + @Override + public synchronized void load(InputStream inStream) throws IOException { + throw new UnsupportedOperationException(PROPERTY_CANNOT_BE_MODIFIED_MSG); + } + + /** + * @see java.util.Properties#load(java.io.Reader) + */ + @Override + public synchronized void load(Reader reader) throws IOException { + throw new UnsupportedOperationException(PROPERTY_CANNOT_BE_MODIFIED_MSG); + } + + /** + * @see java.util.Properties#loadFromXML(java.io.InputStream) + */ + @Override + public synchronized void loadFromXML(InputStream in) throws IOException, InvalidPropertiesFormatException { + throw new UnsupportedOperationException(PROPERTY_CANNOT_BE_MODIFIED_MSG); + } + + /** + * @see java.util.Properties#propertyNames() + */ + @Override + public Enumeration propertyNames() { + return properties.propertyNames(); + } + + /** + * @see java.util.Hashtable#put(java.lang.Object, java.lang.Object) + */ + @Override + public synchronized Object put(Object key, Object value) { + throw new UnsupportedOperationException(PROPERTY_CANNOT_BE_MODIFIED_MSG); + } + + /** + * @see java.util.Hashtable#putAll(java.util.Map) + */ + @Override + public synchronized void putAll(Map t) { + throw new UnsupportedOperationException(PROPERTY_CANNOT_BE_MODIFIED_MSG); + } + + /** + * @see java.util.Hashtable#rehash() + */ + @Override + protected void rehash() { + throw new UnsupportedOperationException(PROPERTY_CANNOT_BE_MODIFIED_MSG); + } + + /** + * @see java.util.Hashtable#remove(java.lang.Object) + */ + @Override + public synchronized Object remove(Object key) { + throw new UnsupportedOperationException(PROPERTY_CANNOT_BE_MODIFIED_MSG); + } + + /** + * @see java.util.Properties#save(java.io.OutputStream, java.lang.String) + */ + @Override + @Deprecated + public synchronized void save(OutputStream out, String comments) { + properties.save(out, comments); + } + + /** + * @see java.util.Properties#setProperty(java.lang.String, java.lang.String) + */ + @Override + public synchronized Object setProperty(String key, String value) { + throw new UnsupportedOperationException(PROPERTY_CANNOT_BE_MODIFIED_MSG); + } + + /** + * @see java.util.Hashtable#size() + */ + @Override + public synchronized int size() { + return properties.size(); + } + + /** + * @see java.util.Properties#store(java.io.OutputStream, java.lang.String) + */ + @Override + public void store(OutputStream out, String comments) throws IOException { + properties.store(out, comments); + } + + /** + * @see java.util.Properties#store(java.io.Writer, java.lang.String) + */ + @Override + public void store(Writer writer, String comments) throws IOException { + properties.store(writer, comments); + } + + /** + * @see java.util.Properties#storeToXML(java.io.OutputStream, java.lang.String) + */ + @Override + public synchronized void storeToXML(OutputStream os, String comment) throws IOException { + properties.storeToXML(os, comment); + } + + /** + * @see java.util.Properties#storeToXML(java.io.OutputStream, java.lang.String, java.lang.String) + */ + @Override + public synchronized void storeToXML(OutputStream os, String comment, String encoding) throws IOException { + properties.storeToXML(os, comment, encoding); + } + + /** + * @see java.util.Properties#stringPropertyNames() + */ + @Override + public Set stringPropertyNames() { + return properties.stringPropertyNames(); + } + + /** + * @see java.util.Hashtable#toString() + */ + @Override + public synchronized String toString() { + return properties.toString(); + } + + /** + * @see java.util.Hashtable#values() + */ + @Override + public Collection values() { + return Collections.unmodifiableCollection(properties.values()); + } +} diff --git a/appc-core/appc-common-bundle/java/org/onap/appc/util/httpClient.java b/appc-core/appc-common-bundle/java/org/onap/appc/util/httpClient.java new file mode 100644 index 000000000..a2ce8af2a --- /dev/null +++ b/appc-core/appc-common-bundle/java/org/onap/appc/util/httpClient.java @@ -0,0 +1,205 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2017 Amdocs + * ============================================================================= + * 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.appc.util; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.MalformedURLException; +import java.net.URL; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.CredentialsProvider; +import org.apache.http.client.methods.HttpDelete; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.onap.appc.configuration.Configuration; +import org.onap.appc.configuration.ConfigurationFactory; +import org.onap.appc.exceptions.APPCException; +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; + + +public class httpClient { + + private static final EELFLogger logger = EELFManager.getInstance().getLogger(httpClient.class); + + private static Configuration configuration = ConfigurationFactory.getConfiguration(); + + @SuppressWarnings("deprecation") + public static int postMethod(String protocol, String ip, int port, String path, String payload, + String contentType) throws APPCException { + + logger.info("Sending POST request to " + path); + + HttpPost post; + try { + + URL serviceUrl = new URL(protocol, ip, port, path); + post = new HttpPost(serviceUrl.toExternalForm()); + post.setHeader("Content-Type", contentType); + + StringEntity entity = new StringEntity(payload); + entity.setContentType(contentType); + post.setEntity(new StringEntity(payload)); + } catch (UnsupportedEncodingException | MalformedURLException e) { + throw new APPCException(e); + } + + logger.debug("Sending request " + post); + + CredentialsProvider credsProvider = new BasicCredentialsProvider(); + credsProvider.setCredentials(new AuthScope(ip, port), new UsernamePasswordCredentials( + configuration.getProperty("username"), configuration.getProperty("password"))); + CloseableHttpClient client = + HttpClients.custom().setDefaultCredentialsProvider(credsProvider).build(); + + int httpCode; + try { + HttpResponse response = client.execute(post); + httpCode = response.getStatusLine().getStatusCode(); + } catch (IOException e) { + throw new APPCException(e); + } + return httpCode; + } + + @SuppressWarnings("deprecation") + public static int putMethod(String protocol, String ip, int port, String path, String payload, + String contentType) throws APPCException { + + logger.info("Sending PUT request to " + path); + + HttpPut put; + try { + + URL serviceUrl = new URL(protocol, ip, port, path); + put = new HttpPut(serviceUrl.toExternalForm()); + put.setHeader("Content-Type", contentType); + + StringEntity entity = new StringEntity(payload); + entity.setContentType(contentType); + put.setEntity(new StringEntity(payload)); + } catch (UnsupportedEncodingException | MalformedURLException e) { + throw new APPCException(e); + } + + logger.debug("Sending request " + put); + + CredentialsProvider credsProvider = new BasicCredentialsProvider(); + credsProvider.setCredentials(new AuthScope(ip, port), new UsernamePasswordCredentials( + configuration.getProperty("username"), configuration.getProperty("password"))); + CloseableHttpClient client = + HttpClients.custom().setDefaultCredentialsProvider(credsProvider).build(); + + int httpCode; + try { + HttpResponse response = client.execute(put); + httpCode = response.getStatusLine().getStatusCode(); + } catch (IOException e) { + throw new APPCException(e); + } + return httpCode; + } + + @SuppressWarnings("deprecation") + public static String getMethod(String protocol, String ip, int port, String path, + String contentType) throws APPCException { + + logger.info("Sending GET request to " + path); + + HttpGet get; + try { + + URL serviceUrl = new URL(protocol, ip, port, path); + get = new HttpGet(serviceUrl.toExternalForm()); + get.setHeader("Content-Type", contentType); + } catch (MalformedURLException e) { + throw new APPCException(e); + } + + logger.debug("Sending request " + get); + + CredentialsProvider credsProvider = new BasicCredentialsProvider(); + credsProvider.setCredentials(new AuthScope(ip, port), new UsernamePasswordCredentials( + configuration.getProperty("username"), configuration.getProperty("password"))); + CloseableHttpClient client = + HttpClients.custom().setDefaultCredentialsProvider(credsProvider).build(); + + int httpCode; + String result; + + try { + HttpResponse response = client.execute(get); + httpCode = response.getStatusLine().getStatusCode(); + result = (httpCode == HttpStatus.SC_OK) ? response.getEntity().toString() : null; + } catch (IOException e) { + throw new APPCException(e); + } + + return result; + } + + @SuppressWarnings("deprecation") + public static int deleteMethod(String protocol, String ip, int port, String path, + String contentType) throws APPCException { + + logger.info("Sending DELETE request to " + path); + + HttpDelete delete; + try { + + URL serviceUrl = new URL(protocol, ip, port, path); + delete = new HttpDelete(serviceUrl.toExternalForm()); + delete.setHeader("Content-Type", contentType); + } catch (MalformedURLException e) { + throw new APPCException(e); + } + + logger.debug("Sending request " + delete); + + CredentialsProvider credsProvider = new BasicCredentialsProvider(); + credsProvider.setCredentials(new AuthScope(ip, port), new UsernamePasswordCredentials( + configuration.getProperty("username"), configuration.getProperty("password"))); + CloseableHttpClient client = + HttpClients.custom().setDefaultCredentialsProvider(credsProvider).build(); + + int httpCode; + + try { + HttpResponse response = client.execute(delete); + httpCode = response.getStatusLine().getStatusCode(); + } catch (IOException e) { + throw new APPCException(e); + } + + return httpCode; + } +}