View Javadoc
1   package org.metricshub.wbem.client;
2   
3   /*-
4    * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
5    * WBEM Java Client
6    * ჻჻჻჻჻჻
7    * Copyright 2023 - 2025 MetricsHub
8    * ჻჻჻჻჻჻
9    * Licensed under the Apache License, Version 2.0 (the "License");
10   * you may not use this file except in compliance with the License.
11   * You may obtain a copy of the License at
12   *
13   *      http://www.apache.org/licenses/LICENSE-2.0
14   *
15   * Unless required by applicable law or agreed to in writing, software
16   * distributed under the License is distributed on an "AS IS" BASIS,
17   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18   * See the License for the specific language governing permissions and
19   * limitations under the License.
20   * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱
21   */
22  
23  import java.net.URL;
24  import java.util.ArrayList;
25  import java.util.LinkedHashSet;
26  import java.util.List;
27  import java.util.Locale;
28  import java.util.Map;
29  import java.util.Set;
30  import java.util.stream.Collectors;
31  import java.util.stream.Stream;
32  import javax.security.auth.Subject;
33  import org.metricshub.wbem.javax.cim.CIMInstance;
34  import org.metricshub.wbem.javax.cim.CIMObjectPath;
35  import org.metricshub.wbem.javax.cim.CIMProperty;
36  import org.metricshub.wbem.javax.wbem.CloseableIterator;
37  import org.metricshub.wbem.javax.wbem.WBEMException;
38  import org.metricshub.wbem.javax.wbem.client.EnumerateResponse;
39  import org.metricshub.wbem.javax.wbem.client.PasswordCredential;
40  import org.metricshub.wbem.javax.wbem.client.UserPrincipal;
41  import org.metricshub.wbem.javax.wbem.client.WBEMClient;
42  import org.metricshub.wbem.javax.wbem.client.WBEMClientConstants;
43  import org.metricshub.wbem.javax.wbem.client.WBEMClientFactory;
44  
45  /**
46   * Matsya WBEM client for query execution.
47   *
48   */
49  public class WbemClient implements AutoCloseable {
50  	private static final Locale[] FIXED_AVAILABLE_LOCALES_ARRAY = { Locale.ENGLISH };
51  
52  	private WBEMClient client;
53  
54  	private CloseableIterator<CIMInstance> iterator;
55  
56  	/**
57  	 * Connect to WBEM client.
58  	 *
59  	 * @param url
60  	 * @param username
61  	 * @param password
62  	 * @param timeout
63  	 * @throws WBEMException
64  	 */
65  	public void connect(final URL url, final String username, final char[] password, int timeout) throws WBEMException {
66  		Utils.checkNonNull(url, "url");
67  		Utils.checkNonNull(username, "username");
68  		Utils.checkNonNull(password, "password");
69  
70  		final CIMObjectPath cimObjectPath = new CIMObjectPath(
71  			url.getProtocol(),
72  			url.getHost(),
73  			String.valueOf(url.getPort()),
74  			null,
75  			null,
76  			null
77  		);
78  
79  		final Subject subject = new Subject();
80  		subject.getPrincipals().add(new UserPrincipal(username));
81  		subject.getPrivateCredentials().add(new PasswordCredential(password));
82  
83  		// Create and initialize a WBEM client.
84  		client = WBEMClientFactory.getClient("CIM-XML");
85  		client.setProperty(WBEMClientConstants.PROP_TIMEOUT, String.valueOf(timeout));
86  		client.initialize(cimObjectPath, subject, FIXED_AVAILABLE_LOCALES_ARRAY);
87  	}
88  
89  	@Override
90  	public void close() {
91  		if (iterator != null) {
92  			iterator.close();
93  		}
94  
95  		if (client != null) {
96  			client.close();
97  		}
98  	}
99  
100 	/**
101 	 * Execute a WQL query on remote.
102 	 * @param wqlQuery The query handler.
103 	 * @param namespace The WBEM namespace.
104 	 * @param arraySeparator The array separator value. default value '|'
105 	 * @return
106 	 * @throws WBEMException
107 	 */
108 	public WbemQueryResult executeWql(final WqlQuery wqlQuery, final String namespace, final String arraySeparator)
109 		throws WBEMException {
110 		Utils.checkNonNull(wqlQuery, "wqlQuery");
111 		Utils.checkNonNull(namespace, "namespace");
112 
113 		if (client == null) {
114 			throw new IllegalStateException("client must be connected first.");
115 		}
116 
117 		iterator =
118 			client.enumerateInstances(
119 				new CIMObjectPath(null, null, null, namespace, wqlQuery.getClassName(), null),
120 				true,
121 				false,
122 				true,
123 				wqlQuery.getPropertiesArray()
124 			);
125 
126 		return enumerateInstances(wqlQuery, iterator, arraySeparator);
127 	}
128 
129 	/**
130 	 * Get associators.
131 	 * @param wqlQuery The query handler.
132 	 * @param objectPathAssociators The object path for ASSOCIATORS.
133 	 * @param arraySeparator The array separator value. default value '|'
134 	 * @return
135 	 * @throws WBEMException
136 	 */
137 	public WbemQueryResult getAssociators(
138 		final WqlQuery wqlQuery,
139 		final String objectPathAssociators,
140 		final String arraySeparator
141 	)
142 		throws WBEMException {
143 		Utils.checkNonNull(wqlQuery, "wqlQuery");
144 		Utils.checkNonNull(objectPathAssociators, "objectPathAssociators");
145 
146 		if (client == null) {
147 			throw new IllegalStateException("client must be connected first.");
148 		}
149 
150 		final EnumerateResponse<CIMInstance> response = client.associators(
151 			new CIMObjectPath(objectPathAssociators),
152 			wqlQuery.getClassName(),
153 			null,
154 			null,
155 			null,
156 			false,
157 			wqlQuery.getPropertiesArray(),
158 			null,
159 			null,
160 			null,
161 			false,
162 			null
163 		);
164 		iterator = response.getResponses();
165 
166 		return enumerateInstances(wqlQuery, iterator, arraySeparator);
167 	}
168 
169 	public static WbemQueryResult enumerateInstances(
170 		final WqlQuery wqlQuery,
171 		final CloseableIterator<CIMInstance> iterator,
172 		final String arraySeparator
173 	) {
174 		if (iterator == null) {
175 			return new WbemQueryResult(new ArrayList<>(), new ArrayList<>());
176 		}
177 
178 		Set<String> properties = null;
179 		List<String> originalProperties = null;
180 		final List<List<String>> values = new ArrayList<>();
181 
182 		while (iterator.hasNext()) {
183 			final CIMInstance cimInstance = iterator.next();
184 
185 			if (properties == null) {
186 				properties =
187 					wqlQuery.getProperties().isEmpty()
188 						? Stream
189 							.of(cimInstance.getProperties())
190 							.map(CIMProperty::getName)
191 							.collect(Collectors.toCollection(LinkedHashSet::new))
192 						: wqlQuery.getProperties();
193 			}
194 
195 			if (originalProperties == null) {
196 				originalProperties =
197 					wqlQuery.hasDuplicateProperties()
198 						? wqlQuery.getOriginalProperties()
199 						: properties.stream().collect(Collectors.toList());
200 			}
201 
202 			final List<String> row;
203 			if (wqlQuery.hasDuplicateProperties()) {
204 				final Map<String, String> cimProperties = properties
205 					.stream()
206 					.collect(
207 						Collectors.toMap(
208 							String::toLowerCase,
209 							property -> WbemCimDataHandler.getCimPropertyAsString(property, cimInstance, arraySeparator)
210 						)
211 					);
212 
213 				row =
214 					originalProperties
215 						.stream()
216 						.map(property -> cimProperties.get(property.toLowerCase()))
217 						.collect(Collectors.toList());
218 			} else {
219 				row =
220 					properties
221 						.stream()
222 						.map(property -> WbemCimDataHandler.getCimPropertyAsString(property, cimInstance, arraySeparator))
223 						.collect(Collectors.toList());
224 			}
225 
226 			values.add(row);
227 		}
228 
229 		return new WbemQueryResult(originalProperties, values);
230 	}
231 }