7828960b4d71f1a683ab65a352266514ff512d32
[policy/apex-pdp.git] / services / services-engine / src / main / java / org / onap / policy / apex / service / engine / event / ApexEvent.java
1 /*-
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
4  * ================================================================================
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  * SPDX-License-Identifier: Apache-2.0
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.policy.apex.service.engine.event;
22
23 import java.io.Serializable;
24 import java.util.HashMap;
25 import java.util.Map;
26 import java.util.Properties;
27 import java.util.concurrent.atomic.AtomicLong;
28
29 import lombok.EqualsAndHashCode;
30 import lombok.Getter;
31 import lombok.ToString;
32
33 import org.onap.policy.apex.model.basicmodel.concepts.AxReferenceKey;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37 /**
38  * The Class ApexEvent is an event class that external systems use to send events to and receive
39  * events from Apex engines. The event itself is a hash map of string keys and object values, used
40  * to pass data.
41  *
42  * @author Liam Fallon (liam.fallon@ericsson.com)
43  */
44 @Getter
45 @ToString
46 @EqualsAndHashCode(callSuper = false)
47 public class ApexEvent extends HashMap<String, Object> implements Serializable {
48     private static final long serialVersionUID = -4451918242101961685L;
49
50     // Get a reference to the logger
51     private static final Logger LOGGER = LoggerFactory.getLogger(ApexEvent.class);
52
53     // Recurring string constants
54     private static final String EVENT_PREAMBLE = "event \"";
55
56     // Holds the next identifier for event execution.
57     private static AtomicLong nextExecutionID = new AtomicLong(0L);
58
59     /**
60      * The name of the Apex event, a mandatory field. All Apex events must have a name so that the
61      * event can be looked up in the Apex policy model.
62      */
63     public static final String NAME_HEADER_FIELD = "name";
64
65     /**
66      * The version of the Apex event, an optional field. If a version is specified on an Apex event,
67      * the definition of that version of the event is taken from the Apex policy model. If no
68      * version is specified, the latest version of the event is used.
69      */
70     public static final String VERSION_HEADER_FIELD = "version";
71
72     /**
73      * The name space of the Apex event, an optional field. If a name space is specified on an Apex
74      * event it must match the name space on the event definition taken from the Apex policy model.
75      * If no name space is specified, the name space from the event definition in the Apex policy
76      * model is used.
77      */
78     public static final String NAMESPACE_HEADER_FIELD = "nameSpace";
79
80     /**
81      * The source of the Apex event, an optional field. It specifies where the Apex event has come
82      * from and its use is reserved for now. If no source is specified, the source from the event
83      * definition in the Apex policy model is used.
84      */
85     public static final String SOURCE_HEADER_FIELD = "source";
86
87     /**
88      * The target of the Apex event, an optional field. It specifies where the Apex event is going
89      * to and its use is reserved for now. If no target is specified, the target from the event
90      * definition in the Apex policy model is used.
91      */
92     public static final String TARGET_HEADER_FIELD = "target";
93
94     /**
95      * The exception message field of an Apex event is an exception message indicating that an event
96      * failed.
97      */
98     public static final String EXCEPTION_MESSAGE_HEADER_FIELD = "exceptionMessage";
99
100     /** The name of an Apex event must match this regular expression. */
101     public static final String NAME_REGEXP = "[A-Za-z0-9\\-_.]+";
102
103     /** The version of an Apex event must match this regular expression. */
104     public static final String VERSION_REGEXP = "[A-Za-z0-9.]+";
105
106     /** The name space of an Apex event must match this regular expression. */
107     public static final String NAMESPACE_REGEXP = "([a-zA_Z_][\\.\\w]*)";
108
109     /** The source of an Apex event must match this regular expression. */
110     public static final String SOURCE_REGEXP = "^$|[A-Za-z0-9\\.\\-_:]+";
111
112     /** The target of an Apex event must match this regular expression. */
113     public static final String TARGET_REGEXP = SOURCE_REGEXP;
114
115     // The standard fields of the event
116     private final String name;
117     private final String version;
118     private final String nameSpace;
119     private final String source;
120     private final String target;
121
122     // An identifier for the current event execution. The default value here will always be unique
123     // in a single JVM
124     private long executionId = ApexEvent.getNextExecutionId();
125
126     // Event related properties used during processing of this event
127     private Properties executionProperties = new Properties();
128
129     // A string holding a message that indicates why processing of this event threw an exception
130     private String exceptionMessage;
131
132     /**
133      * Instantiates a new apex event.
134      *
135      * @param name the name of the event
136      * @param version the version of the event
137      * @param nameSpace the name space (java package) of the event
138      * @param source the source of the event
139      * @param target the target of the event
140      * @throws ApexEventException thrown on validation errors on event names and versions
141      */
142     public ApexEvent(final String name, final String version, final String nameSpace, final String source,
143             final String target) throws ApexEventException {
144         // @formatter:off
145         this.name      = validateField(NAME_HEADER_FIELD,      name,      NAME_REGEXP);
146         this.version   = validateField(VERSION_HEADER_FIELD,   version,   VERSION_REGEXP);
147         this.nameSpace = validateField(NAMESPACE_HEADER_FIELD, nameSpace, NAMESPACE_REGEXP);
148         this.source    = validateField(SOURCE_HEADER_FIELD,    source,    SOURCE_REGEXP);
149         this.target    = validateField(TARGET_HEADER_FIELD,    target,    TARGET_REGEXP);
150         // @formatter:on
151     }
152
153     /**
154      * Private utility to get the next candidate value for a Execution ID. This value will always be
155      * unique in a single JVM
156      *
157      * @return the next candidate value for a Execution ID
158      */
159     private static synchronized long getNextExecutionId() {
160         return nextExecutionID.getAndIncrement();
161     }
162
163     /**
164      * Check that a field of the event is valid.
165      *
166      * @param fieldName the name of the field to check
167      * @param fieldValue the value of the field to check
168      * @param fieldRegexp the regular expression to check the field against
169      * @return the validated field value
170      * @throws ApexEventException thrown if the field is invalid
171      */
172     private String validateField(final String fieldName, final String fieldValue, final String fieldRegexp)
173             throws ApexEventException {
174         if (fieldValue.matches(fieldRegexp)) {
175             return fieldValue;
176         } else {
177             String message = EVENT_PREAMBLE + name + ": field \"" + fieldName + "=" + fieldValue
178                     + "\"  is illegal. It doesn't match regex '" + fieldRegexp + "'";
179             LOGGER.warn(message);
180             throw new ApexEventException(message);
181         }
182     }
183
184     /**
185      * Check that the key of an event is valid.
186      *
187      * @param key the key
188      * @return the string
189      * @throws ApexEventException the apex event exception
190      */
191     private String validKey(final String key) throws ApexEventException {
192         if (key.matches(AxReferenceKey.LOCAL_NAME_REGEXP)) {
193             return key;
194         } else {
195             String message = EVENT_PREAMBLE + name + ": key \"" + key + "\" is illegal";
196             LOGGER.warn(message);
197             throw new ApexEventException(message);
198         }
199     }
200
201     /**
202      * Sets the pass-thru executionID for this event.
203      *
204      * <p>The default value for executionID is unique in the current JVM. For some applications/deployments this
205      * executionID may need to be globally unique
206      *
207      * @param executionId the executionID
208      */
209     public void setExecutionId(final long executionId) {
210         this.executionId = executionId;
211     }
212
213     /**
214      * Set the execution properties for this event.
215      *
216      * @param executionProperties the execution properties to set
217      */
218     public void setExecutionProperties(Properties executionProperties) {
219         this.executionProperties = executionProperties;
220     }
221
222     /**
223      * Sets the exception message explaining why processing of this event to fail.
224      *
225      * @param exceptionMessage the exception message
226      */
227     public void setExceptionMessage(final String exceptionMessage) {
228         this.exceptionMessage = exceptionMessage;
229     }
230
231     /*
232      * Map overrides from here
233      */
234
235     /**
236      * {@inheritDoc}.
237      */
238     @Override
239     public Object put(final String key, final Object value) {
240         // Check if the key is valid
241         try {
242             return super.put(validKey(key), value);
243         } catch (final ApexEventException e) {
244             if (LOGGER.isTraceEnabled()) {
245                 LOGGER.trace("put failed", e);
246             }
247             return null;
248         }
249     }
250
251     /**
252      * {@inheritDoc}.
253      */
254     @Override
255     public void putAll(final Map<? extends String, ? extends Object> incomingMap) {
256         // Check the keys are valid
257         try {
258             for (final String key : incomingMap.keySet()) {
259                 validKey(key);
260             }
261         } catch (final ApexEventException e) {
262             if (LOGGER.isTraceEnabled()) {
263                 LOGGER.trace("putAll failed", e);
264             }
265
266             // One of the keys is invalid
267             return;
268         }
269
270         // Go ahead and put everything
271         super.putAll(incomingMap);
272     }
273 }