WinRM Java Client 3.1.04
-
Home
- Usage
WQL Queries
WQL (WMI Query Language) is the SQL-like language used to query the Windows Management Instrumentation (WMI) repository. The client runs a query on the remote host and returns the rows.
Running a query
Build a WinRMClient[1], then prepare the query with wql(...) and run it with execute():
import java.time.Duration;
import org.metricshub.winrm.WinRMClient;
import org.metricshub.winrm.WqlResult;
import org.metricshub.winrm.WqlRow;
try (WinRMClient client = WinRMClient.builder("server.example.com")
.credentials("DOMAIN\\Administrator", password)
.timeout(Duration.ofSeconds(30))
.build()) {
WqlResult result = client.wql("SELECT Name, State FROM Win32_Service").execute();
for (WqlRow row : result) {
System.out.println(row.string("Name") + " is " + row.string("State"));
}
}
One client can run any number of queries (and commands[2]) over the same authenticated connection — see the client options[3].
Query options
Everything between wql(...) and execute() is optional:
| Option | Default | Meaning |
|---|---|---|
namespace(String) |
the client's namespace (ROOT\CIMV2 unless set on the builder) |
The WMI namespace to query. |
timeout(Duration) |
the client's timeout | Wall-clock deadline for the whole query with execute(); inactivity timeout with stream(). See Timeouts and Errors[4]. |
pageSize(int) |
32000 | WS-Enumeration MaxElements: how many rows the server may return per protocol round trip. |
pullTimeout(Duration) |
server default | WS-Enumeration MaxTime: how long the server may hold a single Pull open before answering with the rows it has. |
WqlResult events = client.wql("SELECT * FROM Win32_NTLogEvent")
.namespace("root\\cimv2")
.pageSize(5000)
.pullTimeout(Duration.ofSeconds(10))
.timeout(Duration.ofMinutes(2))
.execute();
pageSize and pullTimeout matter for very large result sets (for example Windows event logs): a smaller page bounds each response's size, and a pull timeout keeps the server from holding a Pull open past your deadline while it gathers rows.
Streaming the rows
execute() collects the complete result in memory. For very large result sets — Windows event logs, software inventories — end the same request with stream() instead: it returns a lazy java.util.stream.Stream of WqlRow[5]s that yields each row as soon as it is parsed and pulls the next WS-Enumeration page from the server only as the stream advances. Memory stays bounded by one page (pageSize(int)), not by the whole result set.
try (Stream<WqlRow> rows = client.wql("SELECT * FROM Win32_NTLogEvent")
.pageSize(5000)
.stream()) {
rows.filter(row -> "Error".equals(row.string("Type")))
.limit(100)
.forEach(this::process);
}
Points to know:
- Close the stream — use try-with-resources, exactly like
Files.lines(...). Closing before the last row tells the server to free the enumeration immediately (WS-EnumerationRelease); an exhausted stream cleans up on its own. - The stream holds the client's serial connection while open: other operations on the same client wait until it is closed or exhausted (the same contract as a JDBC
ResultSeton its connection). Never call the same client while consuming the stream; use a second client. - The timeout is an inactivity timeout — the longest silence tolerated from the server between two responses — not an overall deadline: consuming a huge result can take arbitrarily long as long as the server keeps answering. See Timeouts and Errors[4].
- The stream is sequential and ordered; failures while iterating are reported through the same unchecked exceptions as
execute()(see below).
Reading the result
WqlResult[6] is immutable and iterable:
| Method | Returns | Description |
|---|---|---|
columns() |
List<String> |
The property (column) names (see Column order and case). |
rows() |
List<WqlRow[5]> |
The result rows. |
size() / isEmpty() |
int / boolean |
Row count. |
elapsed() |
java.time.Duration |
Wall-clock time of the query. |
Each WqlRow[5] exposes the instance properties:
| Method | Returns | Description |
|---|---|---|
string(String) |
String |
The property value, or null when the row has no such property. |
get(String) |
Object |
The same value (always a String). |
asMap() |
Map<String, Object> |
All properties, in server order (unmodifiable). |
Property lookup is case-insensitive, matching WMI semantics: row.string("name") and row.string("Name") return the same value.
Values are the text WinRM sends, unconverted: parse numbers, booleans and dates yourself. A WMI NULL comes back as an empty string.
A WMI array property (IPAddress in Win32_NetworkAdapterConfiguration, Capabilities in Win32_DiskDrive, …) comes back as a single string, its elements joined with |: "192.0.2.10|fe80::1". The builder's arraySeparator(String) changes the separator.
There is no trailing separator, and WinRM sends no type information: a one-element array reads like a scalar ("fr-FR" for MUILanguages), and an empty or NULL array is left out of the response, so string() returns null for it (unlike a scalar NULL, an empty string). The instance is still a row, even when that array is the only selected property.
Object paths: __PATH and references
WinRM never returns WMI system properties, except __PATH: when you select it, the client asks for each instance's key properties and builds its object path. A reference property (Antecedent, Dependent, …) gets the same format, so you can match the two:
SELECT __PATH, CurrentClockSpeed FROM Win32_Processor
-- __PATH = Win32_Processor.DeviceID="CPU0"
The path is relative to the namespace of the query, without server or namespace, like the __PATH and references returned by the WMI client. A reference nested in a key (the __PATH of an association like Win32_SessionProcess) stays absolute, with . (the local machine, for WMI) as the server, since WinRM sends no server name. WinRM sends no key types either: every key value is quoted, even a number. The path works as is in an ASSOCIATORS OF {...} or REFERENCES OF {...} query.
Column order and case
These rules apply to WqlResult.columns(); each row's asMap() keeps the server's order, and lookups in it are case-sensitive.
- When you select explicit properties (
SELECT Name, State FROM ...), the columns keep the order of the query and the exact case reported by WMI. - With
SELECT * FROM ..., the columns are the properties of every row (a row may lack an empty array that others have), in alphabetical order (case-insensitive). - If the query returns no rows, the columns are the selected property names in lower case (WMI's own casing cannot be recovered from an empty result set), or an empty list for
SELECT *.
Supported WQL syntax
The client validates that the query is a simple SELECT, or an ASSOCIATORS OF or REFERENCES OF query:
SELECT * FROM Win32_OperatingSystem
SELECT Name, State, StartMode FROM Win32_Service
SELECT Name FROM Win32_Process WHERE Name = 'explorer.exe'
ASSOCIATORS OF {Win32_Service.Name='W32Time'} WHERE ResultClass = Win32_Process
REFERENCES OF {Win32_Service.Name='W32Time'}
SELECT DeviceId, Wear FROM ASSOCIATORS OF {MSFT_PhysicalDisk.ObjectId="..."} WHERE ResultClass = MSFT_StorageReliabilityCounter
The grammar is a single SELECT of either * or a comma-separated property list, a FROM clause naming one class or an ASSOCIATORS OF {...} / REFERENCES OF {...} statement, and an optional WHERE clause; the SELECT ... FROM part may be left out of an ASSOCIATORS OF / REFERENCES OF statement. WMI itself does not accept a property list on these statements: the client sends the bare statement, and the property list only sets the result's columns (each row still holds every property of the instance). Line breaks are accepted wherever whitespace is, so queries written as Java text blocks work as is. Anything else, such as event queries (WITHIN), is rejected: an invalid query raises a WqlSyntaxException[7] before anything is sent to the host.
Choosing a namespace
Most Windows classes live under the default ROOT\CIMV2 namespace. To query a different one — for example ROOT\Microsoft\SqlServer or ROOT\WMI — set it per query with namespace(...), or set a client-wide default with namespace(...) on the builder. Both ROOT\WMI and ROOT/WMI are accepted.
Exceptions
execute() and stream() report failures as unchecked WinRMClientException[8]s; see Timeouts and Errors[9] for the full list. An unknown class or namespace raises a WinRMFaultException[10] whose getFaultDetail() carries the WMI error, e.g. WBEM_E_INVALID_CLASS or WBEM_E_INVALID_NAMESPACE.
From the command line
The standalone jar runs queries in the default ROOT\CIMV2 namespace through its wql subcommand, streaming the rows to stdout as JSON Lines — see the Command-Line Client[11] manual.
- [1] apidocs/org/metricshub/winrm/WinRMClient.html
- [2] commands.html
- [3] index.html#client-options
- [4] timeouts-and-errors.html
- [5] apidocs/org/metricshub/winrm/WqlRow.html
- [6] apidocs/org/metricshub/winrm/WqlResult.html
- [7] apidocs/org/metricshub/winrm/exceptions/WqlSyntaxException.html
- [8] apidocs/org/metricshub/winrm/exceptions/WinRMClientException.html
- [9] timeouts-and-errors.html#the-exception-surface
- [10] apidocs/org/metricshub/winrm/exceptions/WinRMFaultException.html
- [11] cli.html
