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, ByReference::intValue);
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 static OffsetDateTime convertCimDateTime(final ByReference value) {
99 return Utils.convertCimDateTime(value.stringValue());
100 }
101
102
103
104
105
106
107 static String convertCimReference(final ByReference value) {
108 return WmiCimTypeHandler.convertCimReference(value.stringValue());
109 }
110
111
112
113
114
115
116 static String convertCimReference(final String reference) {
117 if (reference == null) {
118 return null;
119 }
120
121
122 final int colonIndex = reference.indexOf(':');
123 return colonIndex > -1 ? reference.substring(colonIndex + 1) : reference;
124 }
125
126
127
128
129
130
131
132
133
134
135
136 static Map<String, Object> convertSafeArray(
137 final ByReference array,
138 final int cimType,
139 final Entry<String, Set<String>> property
140 ) {
141
142 final SAFEARRAY safeArray = (SAFEARRAY) array.getValue();
143 if (safeArray == null) {
144 return Collections.singletonMap(property.getKey(), null);
145 }
146
147 safeArray.lock();
148
149
150 final int lowerBound = safeArray.getLBound(0);
151 final int length = safeArray.getUBound(0) - lowerBound + 1;
152
153
154 final Object[] resultArray = new Object[length];
155 for (int i = 0; i < length; i++) {
156 resultArray[i] = safeArray.getElement(lowerBound + i);
157 }
158
159 safeArray.unlock();
160
161
162
163 if (cimType == Wbemcli.CIM_REFERENCE) {
164 return Collections.singletonMap(
165 property.getKey(),
166 Stream.of(resultArray).map(String.class::cast).map(WmiCimTypeHandler::convertCimReference).toArray()
167 );
168 }
169 if (cimType == Wbemcli.CIM_DATETIME) {
170 return Collections.singletonMap(
171 property.getKey(),
172 Stream.of(resultArray).map(String.class::cast).map(Utils::convertCimDateTime).toArray()
173 );
174 }
175 if (cimType == Wbemcli.CIM_OBJECT) {
176 if (property.getValue().isEmpty()) {
177 return Collections.singletonMap(property.getKey(), new String[] { CIM_OBJECT_LABEL });
178 }
179
180 final Map<String, List<Object>> resulMap = new HashMap<>();
181
182 for (final Object resultValue : resultArray) {
183 final Optional<IWbemClassObject> maybeClassObject = getUnknownWbemClassObject(resultValue);
184 if (!maybeClassObject.isPresent()) {
185 continue;
186 }
187
188 final Map<String, String> subPropertiesNames = getSubPropertiesNamesFromClass(maybeClassObject.get());
189
190 try {
191 property
192 .getValue()
193 .stream()
194 .map(subProperty -> subPropertiesNames.get(subProperty.toLowerCase()))
195 .forEach(subProperty ->
196 resulMap
197 .computeIfAbsent(buildCimObjectSubPropertyName(property, subProperty), key -> new ArrayList<>())
198 .add(
199 getPropertyValue(
200 maybeClassObject.get(),
201 new AbstractMap.SimpleEntry<String, Set<String>>(subProperty, Collections.emptySet())
202 )
203 .get(subProperty)
204 )
205 );
206 } finally {
207 maybeClassObject.get().Release();
208 }
209 }
210
211 return resulMap.entrySet().stream().collect(Collectors.toMap(Entry::getKey, entry -> entry.getValue().toArray()));
212 }
213
214
215 return Collections.singletonMap(property.getKey(), resultArray);
216 }
217
218
219
220
221
222
223
224
225
226
227
228
229 static Map<String, Object> convertCimObject(final ByReference value, final Entry<String, Set<String>> property) {
230 final Optional<IWbemClassObject> maybeClassObject = getUnknownWbemClassObject(value.getValue());
231 if (!maybeClassObject.isPresent()) {
232 return property
233 .getValue()
234 .stream()
235 .collect(
236 HashMap::new,
237 (map, subProperty) -> map.put(buildCimObjectSubPropertyName(property, subProperty), null),
238 HashMap::putAll
239 );
240 }
241
242 try {
243 final Map<String, String> subPropertiesNames = getSubPropertiesNamesFromClass(maybeClassObject.get());
244
245 return property
246 .getValue()
247 .stream()
248 .map(subProperty -> subPropertiesNames.get(subProperty.toLowerCase()))
249 .collect(
250 HashMap::new,
251 (map, subProperty) ->
252 map.put(
253 buildCimObjectSubPropertyName(property, subProperty),
254 getPropertyValue(
255 maybeClassObject.get(),
256 new AbstractMap.SimpleEntry<String, Set<String>>(subProperty, Collections.emptySet())
257 )
258 .get(subProperty)
259 ),
260 HashMap::putAll
261 );
262 } finally {
263 maybeClassObject.get().Release();
264 }
265 }
266
267
268
269
270
271
272 static Map<String, String> getSubPropertiesNamesFromClass(final IWbemClassObject wbemClassObject) {
273 String[] names;
274 try {
275 names = wbemClassObject.GetNames(null, 0, null);
276 } catch (final Throwable e) {
277 names = wbemClassObject.GetNames(null, 0, null);
278 }
279
280 return Stream.of(names).collect(Collectors.toMap(String::toLowerCase, Function.identity()));
281 }
282
283
284
285
286
287
288
289
290
291 static String buildCimObjectSubPropertyName(final Entry<String, Set<String>> property, final String subProperty) {
292 return new StringBuilder().append(property.getKey()).append(".").append(subProperty).toString();
293 }
294
295
296
297
298
299
300
301 static Optional<IWbemClassObject> getUnknownWbemClassObject(final Object value) {
302 if (value == null) {
303 return Optional.empty();
304 }
305
306 final Unknown unknown = (Unknown) value;
307 try {
308 final PointerByReference pointerByReference = new PointerByReference();
309
310 final HRESULT hResult = unknown.QueryInterface(new REFIID(IUnknown.IID_IUNKNOWN), pointerByReference);
311 return COMUtils.FAILED(hResult)
312 ? Optional.empty()
313 : Optional.of(new IWbemClassObject(pointerByReference.getValue()));
314 } finally {
315 unknown.Release();
316 }
317 }
318
319
320
321
322
323
324
325
326
327
328
329
330 static Map<String, Object> convert(
331 final ByReference value,
332 final int cimType,
333 final Entry<String, Set<String>> property
334 ) {
335 if (value.getValue() == null) {
336 return Collections.singletonMap(property.getKey(), null);
337 }
338
339
340 if ((cimType & Wbemcli.CIM_FLAG_ARRAY) > 0) {
341 return convertSafeArray(value, cimType ^ Wbemcli.CIM_FLAG_ARRAY, property);
342 }
343
344 if (cimType == Wbemcli.CIM_OBJECT) {
345 return property.getValue().isEmpty()
346 ? Collections.singletonMap(property.getKey(), CIM_OBJECT_LABEL)
347 : convertCimObject(value, property);
348 }
349
350 return Collections.singletonMap(
351 property.getKey(),
352 CIMTYPE_TO_CONVERTER_MAP.getOrDefault(cimType, v -> "Unsupported type").apply(value)
353 );
354 }
355
356
357
358
359
360
361
362
363
364
365
366
367 public static Map<String, Object> getPropertyValue(
368 final IWbemClassObject wbemClassObject,
369 final Entry<String, Set<String>> property
370 ) {
371 try {
372 return getPropertyValueFromWbemObject(wbemClassObject, property);
373 } catch (final Throwable e) {
374
375 return getPropertyValueFromWbemObject(wbemClassObject, property);
376 }
377 }
378
379 private static Map<String, Object> getPropertyValueFromWbemObject(
380 final IWbemClassObject wbemClassObject,
381 final Entry<String, Set<String>> property
382 ) {
383 final ByReference value = new ByReference();
384 final IntByReference pType = new IntByReference();
385
386
387
388 OleAuto.INSTANCE.VariantInit(value);
389
390 try {
391 final HRESULT hResult = wbemClassObject.Get(property.getKey(), 0, value, pType, new IntByReference());
392 if (COMUtils.FAILED(hResult)) {
393 return Collections.singletonMap(property.getKey(), null);
394 }
395
396
397 if ("__PATH".equalsIgnoreCase(property.getKey())) {
398 return Collections.singletonMap(property.getKey(), convertCimReference(value));
399 }
400
401 return convert(value, pType.getValue(), property);
402 } finally {
403 try {
404 OleAuto.INSTANCE.VariantClear(value);
405 } catch (final Throwable t) {
406
407 }
408 }
409 }
410 }