2 * ========================LICENSE_START=================================
5 * Copyright (C) 2023 Nordix Foundation
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ========================LICENSE_END===================================
21 package org.oran.pmproducer.filter;
23 import java.lang.invoke.MethodHandles;
24 import java.util.ArrayList;
25 import java.util.Collection;
26 import java.util.HashMap;
27 import java.util.HashSet;
34 import org.oran.pmproducer.filter.PmReport.Event;
35 import org.oran.pmproducer.filter.PmReport.MeasDataCollection;
36 import org.oran.pmproducer.filter.PmReport.MeasInfoList;
37 import org.oran.pmproducer.filter.PmReport.MeasResult;
38 import org.oran.pmproducer.filter.PmReport.MeasTypes;
39 import org.oran.pmproducer.filter.PmReport.MeasValuesList;
40 import org.oran.pmproducer.filter.PmReport.Perf3gppFields;
41 import org.oran.pmproducer.tasks.TopicListener.DataFromTopic;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44 import org.thymeleaf.util.StringUtils;
46 public class PmReportFilter {
47 private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
49 private static com.google.gson.Gson gson = new com.google.gson.GsonBuilder() //
50 .disableHtmlEscaping() //
51 .excludeFieldsWithoutExposeAnnotation() //
54 // excludeFieldsWithoutExposeAnnotation is not needed when parsing and this is a
56 private static com.google.gson.Gson gsonParse = new com.google.gson.GsonBuilder() //
57 .disableHtmlEscaping() //
61 private final FilterData filterData;
64 public static class FilterData {
66 public static class MeasTypeSpec {
67 static MeasTypeSpec empty = new MeasTypeSpec();
69 static MeasTypeSpec empty() {
74 String measuredObjClass;
77 final Set<String> measTypes = new HashSet<>();
80 public boolean equals(Object obj) {
81 return measuredObjClass.equals(obj);
85 public int hashCode() {
86 return measuredObjClass.hashCode();
90 final Set<String> sourceNames = new HashSet<>();
91 final Set<String> measObjInstIds = new HashSet<>();
92 final Collection<MeasTypeSpec> measTypeSpecs = new ArrayList<>();
93 final Set<String> measuredEntityDns = new HashSet<>();
95 public void addMeasTypes(String measObjClass, String... measTypes) {
96 MeasTypeSpec spec = this.findMeasTypeSpec(measObjClass);
98 spec = new MeasTypeSpec();
99 spec.measuredObjClass = measObjClass;
100 this.measTypeSpecs.add(spec);
102 for (String measType : measTypes) {
103 spec.measTypes.add(measType);
107 public void addMeasTypes(String measObjClass, Collection<String> measTypes) {
108 for (String measType : measTypes) {
109 addMeasTypes(measObjClass, measType);
114 String pmRopStartTime;
119 public void addAll(FilterData other) {
120 addAll(other.sourceNames, sourceNames);
121 addAll(other.measObjInstIds, measObjInstIds);
122 addAll(other.measTypeSpecs);
123 addAll(other.measuredEntityDns, measuredEntityDns);
126 public MeasTypeSpec getMeasTypeSpec(String measuredObjClass) {
127 if (measTypeSpecs.isEmpty()) {
128 return MeasTypeSpec.empty();
130 return findMeasTypeSpec(measuredObjClass);
133 private MeasTypeSpec findMeasTypeSpec(String measuredObjClass) {
134 for (MeasTypeSpec t : this.measTypeSpecs) {
135 if (t.measuredObjClass.equals(measuredObjClass)) {
142 private void addAll(Collection<MeasTypeSpec> measTypes) {
143 for (MeasTypeSpec s : measTypes) {
144 addMeasTypes(s.getMeasuredObjClass(), s.getMeasTypes());
148 private void addAll(Set<String> source, Set<String> dst) {
149 if (source.isEmpty()) {
151 } else if (dst.isEmpty()) {
152 // Nothing, this means 'match all'
159 public static PmReport parse(String string) {
160 return gsonParse.fromJson(string, PmReport.class);
163 private static class MeasTypesIndexed extends PmReport.MeasTypes {
165 private Map<String, Integer> map = new HashMap<>();
167 public int addP(String measTypeName) {
168 Integer p = map.get(measTypeName);
172 sMeasTypesList.add(measTypeName);
173 this.map.put(measTypeName, sMeasTypesList.size());
174 return sMeasTypesList.size();
179 public PmReportFilter(FilterData filterData) {
180 this.filterData = filterData;
183 public FilteredData filter(DataFromTopic data) {
185 PmReport report = getPmReport(data);
187 if (report.event == null || report.event.getPerf3gppFields() == null) {
188 logger.warn("Received PM report with no perf3gppFields, ignored. {}", data);
189 return FilteredData.empty();
192 PmReport reportFiltered = filter(report, this.filterData);
193 if (reportFiltered == null) {
194 return FilteredData.empty();
196 return new FilteredData(reportFiltered.event.getCommonEventHeader().getSourceName(), data.infoTypeId,
197 data.key, gson.toJson(reportFiltered).getBytes());
198 } catch (Exception e) {
199 logger.warn("Could not parse PM data. {}, reason: {}", data, e.getMessage());
200 return FilteredData.empty();
204 @SuppressWarnings("java:S2445") // "data" is a method parameter, and should not be used for synchronization.
205 private PmReport getPmReport(DataFromTopic data) {
206 synchronized (data) {
207 if (data.getCachedPmReport() == null) {
208 data.setCachedPmReport(parse((data.valueAsString())));
210 return data.getCachedPmReport();
215 * Updates the report based on the filter data.
219 * @return true if there is anything left in the report
221 private PmReport filter(PmReport report, FilterData filterData) {
222 if (!matchSourceNames(report, filterData.sourceNames)) {
226 Collection<MeasInfoList> filteredMeasObjs = createMeasObjInstIds(report, filterData);
227 if (filteredMeasObjs.isEmpty()) {
230 MeasDataCollection measDataCollection = report.event.getPerf3gppFields().getMeasDataCollection().toBuilder() //
231 .measInfoList(filteredMeasObjs) //
234 Perf3gppFields perf3gppFields =
235 report.event.getPerf3gppFields().toBuilder().measDataCollection(measDataCollection) //
237 Event event = report.event.toBuilder() //
238 .perf3gppFields(perf3gppFields) //
241 return report.toBuilder() //
246 private boolean isContainedInAny(String aString, Collection<String> collection) {
247 for (String s : collection) {
248 if (StringUtils.contains(aString, s) == Boolean.TRUE) {
255 private boolean isMeasResultMatch(MeasResult measResult, MeasTypes measTypes,
256 FilterData.MeasTypeSpec measTypesSpec) {
257 String measType = measTypes.getMeasType(measResult.getP());
258 return measTypesSpec.measTypes.isEmpty() || measTypesSpec.measTypes.contains(measType);
261 private Collection<MeasResult> createMeasResults(Collection<MeasResult> oldMeasResults, MeasTypes measTypes,
262 FilterData.MeasTypeSpec measTypesSpec) {
263 Collection<MeasResult> newMeasResults = new ArrayList<>();
265 for (MeasResult measResult : oldMeasResults) {
266 if (isMeasResultMatch(measResult, measTypes, measTypesSpec)) {
267 newMeasResults.add(measResult.toBuilder().build());
270 return newMeasResults;
273 private boolean isMeasInstIdMatch(String measObjInstId, FilterData filter) {
274 return filter.measObjInstIds.isEmpty() || isContainedInAny(measObjInstId, filter.measObjInstIds);
277 private String managedObjectClass(String distinguishedName) {
278 int lastRdn = distinguishedName.lastIndexOf(",");
282 int lastEqualChar = distinguishedName.indexOf("=", lastRdn);
283 if (lastEqualChar == -1) {
286 return distinguishedName.substring(lastRdn + 1, lastEqualChar);
289 private FilterData.MeasTypeSpec getMeasTypeSpec(String measObjInstId, FilterData filter) {
290 String measObjClass = managedObjectClass(measObjInstId);
291 return filter.getMeasTypeSpec(measObjClass);
294 private MeasValuesList createMeasValuesList(MeasValuesList oldMeasValues, MeasTypes measTypes, FilterData filter) {
295 FilterData.MeasTypeSpec measTypesSpec = getMeasTypeSpec(oldMeasValues.getMeasObjInstId(), filter);
296 if (measTypesSpec == null) {
297 return MeasValuesList.empty();
300 if (!isMeasInstIdMatch(oldMeasValues.getMeasObjInstId(), filter)) {
301 return MeasValuesList.empty();
304 Collection<MeasResult> newResults = createMeasResults(oldMeasValues.getMeasResults(), measTypes, measTypesSpec);
305 return oldMeasValues.toBuilder() //
306 .measResults(newResults) //
310 private MeasTypes createMeasTypes(Collection<MeasValuesList> newMeasValues, MeasTypes oldMMeasTypes) {
311 MeasTypesIndexed newMeasTypes = new MeasTypesIndexed();
312 for (MeasValuesList l : newMeasValues) {
313 for (MeasResult r : l.getMeasResults()) {
314 String measTypeName = oldMMeasTypes.getMeasType(r.getP());
315 int newP = newMeasTypes.addP(measTypeName);
322 private MeasInfoList createMeasInfoList(MeasInfoList oldMeasInfoList, FilterData filter) {
324 Collection<MeasValuesList> measValueLists = new ArrayList<>();
325 for (MeasValuesList oldValues : oldMeasInfoList.getMeasValuesList()) {
326 MeasValuesList newMeasValues = createMeasValuesList(oldValues, oldMeasInfoList.getMeasTypes(), filter);
327 if (!newMeasValues.isEmpty()) {
328 measValueLists.add(newMeasValues);
332 MeasTypes newMeasTypes = createMeasTypes(measValueLists, oldMeasInfoList.getMeasTypes());
334 return oldMeasInfoList.toBuilder() //
335 .measTypes(newMeasTypes).measValuesList(measValueLists) //
340 private boolean matchMeasuredEntityDns(PmReport report, FilterData filter) {
341 return filter.measuredEntityDns.isEmpty()
342 || this.isContainedInAny(report.event.getPerf3gppFields().getMeasDataCollection().getMeasuredEntityDn(),
343 filter.measuredEntityDns);
346 private Collection<MeasInfoList> createMeasObjInstIds(PmReport report, FilterData filter) {
347 Collection<MeasInfoList> newList = new ArrayList<>();
348 if (!matchMeasuredEntityDns(report, filter)) {
351 for (MeasInfoList oldMeasInfoList : report.event.getPerf3gppFields().getMeasDataCollection()
352 .getMeasInfoList()) {
353 MeasInfoList l = createMeasInfoList(oldMeasInfoList, filter);
354 if (!l.getMeasValuesList().isEmpty()) {
361 private boolean matchSourceNames(PmReport report, Collection<String> sourceNames) {
362 return sourceNames.isEmpty() || sourceNames.contains(report.event.getCommonEventHeader().getSourceName());