1 package org.metricshub.wmi.wbem;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 import com.sun.jna.platform.win32.COM.COMUtils;
24 import com.sun.jna.platform.win32.COM.IUnknown;
25 import com.sun.jna.platform.win32.COM.Unknown;
26 import com.sun.jna.platform.win32.COM.Wbemcli;
27 import com.sun.jna.platform.win32.COM.Wbemcli.IWbemClassObject;
28 import com.sun.jna.platform.win32.Guid.REFIID;
29 import com.sun.jna.platform.win32.OaIdl.SAFEARRAY;
30 import com.sun.jna.platform.win32.OleAuto;
31 import com.sun.jna.platform.win32.Variant.VARIANT.ByReference;
32 import com.sun.jna.platform.win32.WinNT.HRESULT;
33 import com.sun.jna.ptr.IntByReference;
34 import com.sun.jna.ptr.PointerByReference;
35 import java.time.OffsetDateTime;
36 import java.util.AbstractMap;
37 import java.util.ArrayList;
38 import java.util.Collections;
39 import java.util.HashMap;
40 import java.util.List;
41 import java.util.Map;
42 import java.util.Map.Entry;
43 import java.util.Optional;
44 import java.util.Set;
45 import java.util.function.Function;
46 import java.util.stream.Collectors;
47 import java.util.stream.Stream;
48 import org.metricshub.wmi.Utils;
49
50 public class WmiCimTypeHandler {
51
52
53
54
55 private WmiCimTypeHandler() {}
56
57
58
59
60 private static final Map<Integer, Function<ByReference, Object>> CIMTYPE_TO_CONVERTER_MAP;
61
62 static {
63 final Map<Integer, Function<ByReference, Object>> map = new HashMap<>();
64
65 map.put(Wbemcli.CIM_EMPTY, value -> null);
66
67 map.put(Wbemcli.CIM_BOOLEAN, ByReference::booleanValue);
68
69 map.put(Wbemcli.CIM_UINT8, ByReference::byteValue);
70 map.put(Wbemcli.CIM_UINT16, ByReference::intValue);
71 map.put(Wbemcli.CIM_UINT32, WmiCimTypeHandler::convertCimUint32);
72 map.put(Wbemcli.CIM_UINT64, ByReference::stringValue);
73 map.put(Wbemcli.CIM_SINT8, ByReference::shortValue);
74 map.put(Wbemcli.CIM_SINT16, ByReference::shortValue);
75 map.put(Wbemcli.CIM_SINT32, ByReference::intValue);
76 map.put(Wbemcli.CIM_SINT64, ByReference::stringValue);
77
78 map.put(Wbemcli.CIM_REAL32, ByReference::floatValue);
79 map.put(Wbemcli.CIM_REAL64, ByReference::doubleValue);
80
81 map.put(Wbemcli.CIM_CHAR16, ByReference::shortValue);
82 map.put(Wbemcli.CIM_STRING, ByReference::stringValue);
83
84 map.put(Wbemcli.CIM_REFERENCE, WmiCimTypeHandler::convertCimReference);
85 map.put(Wbemcli.CIM_DATETIME, WmiCimTypeHandler::convertCimDateTime);
86
87 CIMTYPE_TO_CONVERTER_MAP = Collections.unmodifiableMap(map);
88 }
89
90 private static final String CIM_OBJECT_LABEL = "CIM_OBJECT";
91
92
93
94
95
96
97
98
99
100
101
102
103
104 private static long convertCimUint32(final ByReference cimUint32Value) {
105 return Integer.toUnsignedLong(cimUint32Value.intValue());
106 }
107
108
109
110
111
112
113
114 static OffsetDateTime convertCimDateTime(final ByReference value) {
115 return Utils.convertCimDateTime(value.stringValue());
116 }
117
118
119
120
121
122
123 static String convertCimReference(final ByReference value) {
124 return WmiCimTypeHandler.convertCimReference(value.stringValue());
125 }
126
127
128
129
130
131
132 static String convertCimReference(final String reference) {
133 if (reference == null) {
134 return null;
135 }
136
137
138 final int colonIndex = reference.indexOf(':');
139 return colonIndex > -1 ? reference.substring(colonIndex + 1) : reference;
140 }
141
142
143
144
145
146
147
148
149
150
151
152 static Map<String, Object> convertSafeArray(
153 final ByReference array,
154 final int cimType,
155 final Entry<String, Set<String>> property
156 ) {
157
158 final SAFEARRAY safeArray = (SAFEARRAY) array.getValue();
159 if (safeArray == null) {
160 return Collections.singletonMap(property.getKey(), null);
161 }
162
163 safeArray.lock();
164
165
166 final int lowerBound = safeArray.getLBound(0);
167 final int length = safeArray.getUBound(0) - lowerBound + 1;
168
169
170 final Object[] resultArray = new Object[length];
171 for (int i = 0; i < length; i++) {
172 resultArray[i] = safeArray.getElement(lowerBound + i);
173 }
174
175 safeArray.unlock();
176
177
178
179 if (cimType == Wbemcli.CIM_REFERENCE) {
180 return Collections.singletonMap(
181 property.getKey(),
182 Stream.of(resultArray).map(String.class::cast).map(WmiCimTypeHandler::convertCimReference).toArray()
183 );
184 }
185 if (cimType == Wbemcli.CIM_DATETIME) {
186 return Collections.singletonMap(
187 property.getKey(),
188 Stream.of(resultArray).map(String.class::cast).map(Utils::convertCimDateTime).toArray()
189 );
190 }
191 if (cimType == Wbemcli.CIM_OBJECT) {
192 if (property.getValue().isEmpty()) {
193 return Collections.singletonMap(property.getKey(), new String[] { CIM_OBJECT_LABEL });
194 }
195
196 final Map<String, List<Object>> resulMap = new HashMap<>();
197
198 for (final Object resultValue : resultArray) {
199 final Optional<IWbemClassObject> maybeClassObject = getUnknownWbemClassObject(resultValue);
200 if (!maybeClassObject.isPresent()) {
201 continue;
202 }
203
204 final Map<String, String> subPropertiesNames = getSubPropertiesNamesFromClass(maybeClassObject.get());
205
206 try {
207 property
208 .getValue()
209 .stream()
210 .map(subProperty -> subPropertiesNames.get(subProperty.toLowerCase()))
211 .forEach(subProperty ->
212 resulMap
213 .computeIfAbsent(buildCimObjectSubPropertyName(property, subProperty), key -> new ArrayList<>())
214 .add(
215 getPropertyValue(
216 maybeClassObject.get(),
217 new AbstractMap.SimpleEntry<String, Set<String>>(subProperty, Collections.emptySet())
218 )
219 .get(subProperty)
220 )
221 );
222 } finally {
223 maybeClassObject.get().Release();
224 }
225 }
226
227 return resulMap.entrySet().stream().collect(Collectors.toMap(Entry::getKey, entry -> entry.getValue().toArray()));
228 }
229
230
231 return Collections.singletonMap(property.getKey(), resultArray);
232 }
233
234
235
236
237
238
239
240
241
242
243
244
245 static Map<String, Object> convertCimObject(final ByReference value, final Entry<String, Set<String>> property) {
246 final Optional<IWbemClassObject> maybeClassObject = getUnknownWbemClassObject(value.getValue());
247 if (!maybeClassObject.isPresent()) {
248 return property
249 .getValue()
250 .stream()
251 .collect(
252 HashMap::new,
253 (map, subProperty) -> map.put(buildCimObjectSubPropertyName(property, subProperty), null),
254 HashMap::putAll
255 );
256 }
257
258 try {
259 final Map<String, String> subPropertiesNames = getSubPropertiesNamesFromClass(maybeClassObject.get());
260
261 return property
262 .getValue()
263 .stream()
264 .map(subProperty -> subPropertiesNames.get(subProperty.toLowerCase()))
265 .collect(
266 HashMap::new,
267 (map, subProperty) ->
268 map.put(
269 buildCimObjectSubPropertyName(property, subProperty),
270 getPropertyValue(
271 maybeClassObject.get(),
272 new AbstractMap.SimpleEntry<String, Set<String>>(subProperty, Collections.emptySet())
273 )
274 .get(subProperty)
275 ),
276 HashMap::putAll
277 );
278 } finally {
279 maybeClassObject.get().Release();
280 }
281 }
282
283
284
285
286
287
288 static Map<String, String> getSubPropertiesNamesFromClass(final IWbemClassObject wbemClassObject) {
289 String[] names;
290 try {
291 names = wbemClassObject.GetNames(null, 0, null);
292 } catch (final Throwable e) {
293 names = wbemClassObject.GetNames(null, 0, null);
294 }
295
296 return Stream.of(names).collect(Collectors.toMap(String::toLowerCase, Function.identity()));
297 }
298
299
300
301
302
303
304
305
306
307 static String buildCimObjectSubPropertyName(final Entry<String, Set<String>> property, final String subProperty) {
308 return new StringBuilder().append(property.getKey()).append(".").append(subProperty).toString();
309 }
310
311
312
313
314
315
316
317 static Optional<IWbemClassObject> getUnknownWbemClassObject(final Object value) {
318 if (value == null) {
319 return Optional.empty();
320 }
321
322 final Unknown unknown = (Unknown) value;
323 try {
324 final PointerByReference pointerByReference = new PointerByReference();
325
326 final HRESULT hResult = unknown.QueryInterface(new REFIID(IUnknown.IID_IUNKNOWN), pointerByReference);
327 return COMUtils.FAILED(hResult)
328 ? Optional.empty()
329 : Optional.of(new IWbemClassObject(pointerByReference.getValue()));
330 } finally {
331 unknown.Release();
332 }
333 }
334
335
336
337
338
339
340
341
342
343
344
345
346 static Map<String, Object> convert(
347 final ByReference value,
348 final int cimType,
349 final Entry<String, Set<String>> property
350 ) {
351 if (value.getValue() == null) {
352 return Collections.singletonMap(property.getKey(), null);
353 }
354
355
356 if ((cimType & Wbemcli.CIM_FLAG_ARRAY) > 0) {
357 return convertSafeArray(value, cimType ^ Wbemcli.CIM_FLAG_ARRAY, property);
358 }
359
360 if (cimType == Wbemcli.CIM_OBJECT) {
361 return property.getValue().isEmpty()
362 ? Collections.singletonMap(property.getKey(), CIM_OBJECT_LABEL)
363 : convertCimObject(value, property);
364 }
365
366 return Collections.singletonMap(
367 property.getKey(),
368 CIMTYPE_TO_CONVERTER_MAP.getOrDefault(cimType, v -> "Unsupported type").apply(value)
369 );
370 }
371
372
373
374
375
376
377
378
379
380
381
382
383 public static Map<String, Object> getPropertyValue(
384 final IWbemClassObject wbemClassObject,
385 final Entry<String, Set<String>> property
386 ) {
387 try {
388 return getPropertyValueFromWbemObject(wbemClassObject, property);
389 } catch (final Throwable e) {
390
391 return getPropertyValueFromWbemObject(wbemClassObject, property);
392 }
393 }
394
395 private static Map<String, Object> getPropertyValueFromWbemObject(
396 final IWbemClassObject wbemClassObject,
397 final Entry<String, Set<String>> property
398 ) {
399 final ByReference value = new ByReference();
400 final IntByReference pType = new IntByReference();
401
402
403
404 OleAuto.INSTANCE.VariantInit(value);
405
406 try {
407 final HRESULT hResult = wbemClassObject.Get(property.getKey(), 0, value, pType, new IntByReference());
408 if (COMUtils.FAILED(hResult)) {
409 return Collections.singletonMap(property.getKey(), null);
410 }
411
412
413 if ("__PATH".equalsIgnoreCase(property.getKey())) {
414 return Collections.singletonMap(property.getKey(), convertCimReference(value));
415 }
416
417 return convert(value, pType.getValue(), property);
418 } finally {
419 try {
420 OleAuto.INSTANCE.VariantClear(value);
421 } catch (final Throwable t) {
422
423 }
424 }
425 }
426 }