1 package org.metricshub.winrm.service;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 import jakarta.xml.bind.JAXBElement;
24 import jakarta.xml.ws.BindingProvider;
25 import jakarta.xml.ws.soap.SOAPFaultException;
26 import java.io.IOException;
27 import java.io.StringWriter;
28 import java.io.Writer;
29 import java.lang.reflect.Proxy;
30 import java.math.BigDecimal;
31 import java.nio.charset.Charset;
32 import java.nio.charset.StandardCharsets;
33 import java.nio.file.Path;
34 import java.text.DecimalFormat;
35 import java.text.DecimalFormatSymbols;
36 import java.util.ArrayList;
37 import java.util.Collections;
38 import java.util.HashMap;
39 import java.util.List;
40 import java.util.Map;
41 import java.util.Objects;
42 import java.util.Optional;
43 import java.util.concurrent.ConcurrentHashMap;
44 import java.util.concurrent.ExecutionException;
45 import java.util.concurrent.TimeoutException;
46 import java.util.concurrent.atomic.AtomicInteger;
47 import java.util.stream.Collectors;
48 import java.util.stream.IntStream;
49 import javax.xml.namespace.QName;
50 import javax.xml.parsers.DocumentBuilderFactory;
51 import javax.xml.parsers.ParserConfigurationException;
52 import javax.xml.xpath.XPath;
53 import javax.xml.xpath.XPathExpressionException;
54 import javax.xml.xpath.XPathFactory;
55 import org.apache.cxf.Bus;
56 import org.apache.cxf.Bus.BusState;
57 import org.apache.cxf.BusFactory;
58 import org.apache.cxf.endpoint.Client;
59 import org.apache.cxf.transport.http.asyncclient.AsyncHTTPConduit;
60 import org.apache.cxf.transport.http.asyncclient.AsyncHTTPConduitFactory;
61 import org.apache.cxf.transport.http.asyncclient.AsyncHTTPConduitFactory.UseAsyncPolicy;
62 import org.metricshub.winrm.Utils;
63 import org.metricshub.winrm.WindowsRemoteCommandResult;
64 import org.metricshub.winrm.WindowsRemoteExecutor;
65 import org.metricshub.winrm.WmiHelper;
66 import org.metricshub.winrm.exceptions.WinRMException;
67 import org.metricshub.winrm.exceptions.WqlQuerySyntaxException;
68 import org.metricshub.winrm.service.client.WinRMInvocationHandler;
69 import org.metricshub.winrm.service.client.auth.AuthenticationEnum;
70 import org.metricshub.winrm.service.enumeration.Enumerate;
71 import org.metricshub.winrm.service.enumeration.EnumerateResponse;
72 import org.metricshub.winrm.service.enumeration.EnumerationContextType;
73 import org.metricshub.winrm.service.enumeration.FilterType;
74 import org.metricshub.winrm.service.enumeration.Pull;
75 import org.metricshub.winrm.service.enumeration.PullResponse;
76 import org.metricshub.winrm.service.shell.CommandLine;
77 import org.metricshub.winrm.service.shell.CommandStateType;
78 import org.metricshub.winrm.service.shell.DesiredStreamType;
79 import org.metricshub.winrm.service.shell.Receive;
80 import org.metricshub.winrm.service.shell.ReceiveResponse;
81 import org.metricshub.winrm.service.shell.Shell;
82 import org.metricshub.winrm.service.shell.StreamType;
83 import org.metricshub.winrm.service.transfer.ResourceCreated;
84 import org.metricshub.winrm.service.wsman.AnyListType;
85 import org.metricshub.winrm.service.wsman.CommandResponse;
86 import org.metricshub.winrm.service.wsman.Delete;
87 import org.metricshub.winrm.service.wsman.Locale;
88 import org.metricshub.winrm.service.wsman.MixedDataType;
89 import org.metricshub.winrm.service.wsman.OptionSetType;
90 import org.metricshub.winrm.service.wsman.OptionType;
91 import org.metricshub.winrm.service.wsman.SelectorSetType;
92 import org.metricshub.winrm.service.wsman.SelectorType;
93 import org.metricshub.winrm.service.wsman.Signal;
94 import org.w3c.dom.Document;
95 import org.w3c.dom.Element;
96 import org.w3c.dom.Node;
97 import org.w3c.dom.NodeList;
98
99 public class WinRMService implements WindowsRemoteExecutor {
100
101 public static final List<AuthenticationEnum> DEFAULT_AUTHENTICATION = Collections.singletonList(
102 AuthenticationEnum.NTLM
103 );
104
105 private static final String STDERR = "stderr";
106 private static final String STDOUT = "stdout";
107
108 private static final int MAX_ENVELOPE_SIZE = 153600;
109
110 private static final String ENUMERATION_NAMESPACE = "http://schemas.xmlsoap.org/ws/2004/09/enumeration";
111
112 private static final String WSMAN_URI = "http://schemas.microsoft.com/wbem/wsman/1";
113
114 private static final String DIALECT_WQL = WSMAN_URI + "/WQL";
115
116 private static final String SHELL_URI = WSMAN_URI + "/windows/shell";
117 private static final String COMMAND_RESOURCE_URI = SHELL_URI + "/cmd";
118 private static final String COMMAND_STATE_DONE = SHELL_URI + "/CommandState/Done";
119 private static final String TERMINATE_CODE = SHELL_URI + "/signal/terminate";
120
121 private static final QName WSEN_ITEMS_QNAME = new QName(ENUMERATION_NAMESPACE, "Items");
122
123 private static final QName WSMAN_ITEMS_QNAME = new QName(WinRMInvocationHandler.WSMAN_SCHEMA_NAMESPACE, "Items");
124
125 private static final QName WSMAN_END_OF_SEQUENCE_QNAME = new QName(
126 WinRMInvocationHandler.WSMAN_SCHEMA_NAMESPACE,
127 "EndOfSequence"
128 );
129
130 private static final QName WSEN_END_OF_SEQUENCE_QNAME = new QName(ENUMERATION_NAMESPACE, "EndOfSequence");
131
132 private static final QName WSMAN_XML_FRAGMENT_QNAME = new QName(
133 WinRMInvocationHandler.WSMAN_SCHEMA_NAMESPACE,
134 "XmlFragment"
135 );
136
137 private static final DocumentBuilderFactory DOCUMENT_BUILDER_FACTORY = DocumentBuilderFactory.newInstance();
138
139
140
141
142
143
144 private static final String WSMAN_FAULT_CODE_OPERATION_TIMEOUT_EXPIRED = "2150858793";
145
146
147
148
149
150
151
152 private static final String WSMAN_FAULT_CODE_SHELL_WAS_NOT_FOUND = "2150858843";
153
154 private static final Locale LOCALE;
155
156 static {
157 LOCALE = new Locale();
158 LOCALE.setLang(java.util.Locale.US.toLanguageTag());
159 }
160
161 private static final OptionSetType OPTION_SET_CREATE;
162
163 static {
164 final OptionType optNoProfile = new OptionType();
165 optNoProfile.setName("WINRS_NOPROFILE");
166 optNoProfile.setValue("true");
167
168 final OptionType optCodepage = new OptionType();
169 optCodepage.setName("WINRS_CODEPAGE");
170 optCodepage.setValue("437");
171
172 OPTION_SET_CREATE = new OptionSetType();
173 OPTION_SET_CREATE.getOption().add(optNoProfile);
174 OPTION_SET_CREATE.getOption().add(optCodepage);
175 }
176
177 private static final OptionSetType OPTION_SET_COMMAND;
178
179 static {
180 final OptionType optConsoleModeStdin = new OptionType();
181 optConsoleModeStdin.setName("WINRS_CONSOLEMODE_STDIN");
182 optConsoleModeStdin.setValue("true");
183
184 final OptionType optSkipCmdShell = new OptionType();
185 optSkipCmdShell.setName("WINRS_SKIP_CMD_SHELL");
186 optSkipCmdShell.setValue("false");
187
188 OPTION_SET_COMMAND = new OptionSetType();
189 OPTION_SET_COMMAND.getOption().add(optConsoleModeStdin);
190 OPTION_SET_COMMAND.getOption().add(optSkipCmdShell);
191 }
192
193 private static final ConcurrentHashMap<WinRMEndpoint, WinRMService> CONNECTIONS_CACHE = new ConcurrentHashMap<>();
194
195 private final AtomicInteger useCount = new AtomicInteger(1);
196
197 private final WinRMEndpoint winRMEndpoint;
198 private final Bus bus;
199 private final WinRMWebService cmdWS;
200 private final WinRMWebService wqlWS;
201 private final Client cmdClient;
202 private final Client wqlClient;
203 private final String strTimeout;
204
205 private SelectorSetType shellSelector = null;
206
207
208
209
210
211
212
213
214
215
216 private WinRMService(
217 final WinRMEndpoint winRMEndpoint,
218 final Bus bus,
219 final WinRMInvocationHandler cmdInvocation,
220 final WinRMInvocationHandler wqlInvocation,
221 final long timeout
222 ) {
223 this.winRMEndpoint = winRMEndpoint;
224 this.bus = bus;
225 this.cmdWS = createProxyService(cmdInvocation);
226 this.wqlWS = createProxyService(wqlInvocation);
227 this.cmdClient = cmdInvocation.getClient();
228 this.wqlClient = wqlInvocation.getClient();
229
230 final BigDecimal timeoutSec = BigDecimal.valueOf(timeout).divide(BigDecimal.valueOf(1000));
231 final DecimalFormat decimalFormat = new DecimalFormat("PT#.###S", new DecimalFormatSymbols(java.util.Locale.ROOT));
232 this.strTimeout = decimalFormat.format(timeoutSec);
233 }
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248 public static WinRMService createInstance(
249 final WinRMEndpoint winRMEndpoint,
250 final long timeout,
251 final Path ticketCache,
252 final List<AuthenticationEnum> authentications
253 ) throws WinRMException {
254 Utils.checkNonNull(winRMEndpoint, "winRMEndpoint");
255 Utils.checkArgumentNotZeroOrNegative(timeout, "timeout");
256
257 final List<AuthenticationEnum> normalizedAuthentications = authentications == null
258 ? DEFAULT_AUTHENTICATION
259 : authentications.stream().distinct().collect(Collectors.toList());
260
261 try {
262 return CONNECTIONS_CACHE.compute(
263 winRMEndpoint,
264 (key, win) -> {
265 if (win == null) {
266 final Bus bus = BusFactory.newInstance().createBus();
267
268
269
270
271 bus.getProperties().put(AsyncHTTPConduit.USE_ASYNC, Boolean.TRUE);
272 bus.getProperties().put(AsyncHTTPConduitFactory.USE_POLICY, UseAsyncPolicy.ALWAYS);
273
274 final WinRMInvocationHandler cmdInvocation = createWinRMInvocationHandlerInstance(
275 winRMEndpoint,
276 bus,
277 timeout,
278 null,
279 ticketCache,
280 normalizedAuthentications
281 );
282
283 final WinRMInvocationHandler wqlInvocation = createWinRMInvocationHandlerInstance(
284 winRMEndpoint,
285 bus,
286 timeout,
287 String.format("%s/wmi/%s/*", WSMAN_URI, winRMEndpoint.getNamespace()),
288 ticketCache,
289 normalizedAuthentications
290 );
291
292 return new WinRMService(winRMEndpoint, bus, cmdInvocation, wqlInvocation, timeout);
293 } else {
294 synchronized (win) {
295 win.incrementUseCount();
296
297 return win;
298 }
299 }
300 }
301 );
302 } catch (final RuntimeException e) {
303 if (e.getCause() != null) {
304 final String message = e.getMessage() != null
305 ? String.format(
306 "%s\n%s: %s",
307 e.getMessage(),
308 e.getCause().getClass().getSimpleName(),
309 e.getCause().getMessage()
310 )
311 : String.format("%s: %s", e.getCause().getClass().getSimpleName(), e.getCause().getMessage());
312 throw new WinRMException(e.getCause(), message);
313 }
314
315 throw new WinRMException(e.getMessage());
316 }
317 }
318
319 public int getUseCount() {
320 return useCount.get();
321 }
322
323
324
325
326 public boolean isConnected() {
327 return getUseCount() > 0;
328 }
329
330 void incrementUseCount() {
331 useCount.incrementAndGet();
332 }
333
334
335
336
337 public void checkConnectedFirst() {
338 if (!isConnected()) {
339 throw new IllegalStateException("This instance has been closed and a new one must be created.");
340 }
341 }
342
343 @Override
344 public void close() {
345 if (useCount.decrementAndGet() == 0) {
346 CONNECTIONS_CACHE.remove(winRMEndpoint);
347
348 if (shellSelector != null) {
349 cmdWS.delete(new Delete(), COMMAND_RESOURCE_URI, MAX_ENVELOPE_SIZE, strTimeout, LOCALE, shellSelector);
350
351 shellSelector = null;
352 }
353
354 if (cmdClient != null) {
355 cmdClient.destroy();
356 }
357
358 if (wqlClient != null) {
359 wqlClient.destroy();
360 }
361
362 if (bus != null && bus.getState() != BusState.SHUTDOWN) {
363 bus.shutdown(true);
364 }
365 }
366 }
367
368 @Override
369 public WindowsRemoteCommandResult executeCommand(
370 final String command,
371 final String workingDirectory,
372 final Charset charset,
373 final long timeout
374 ) throws WinRMException, TimeoutException {
375 Utils.checkNonNull(command, "command");
376 Utils.checkArgumentNotZeroOrNegative(timeout, "timeout");
377
378 checkConnectedFirst();
379
380 try {
381 return Utils.execute(
382 () -> {
383 if (getShellSelector() == null) {
384 create(workingDirectory);
385 }
386
387 try {
388 final StringWriter stdout = new StringWriter();
389 final StringWriter stderr = new StringWriter();
390 final Charset cs = charset != null ? charset : StandardCharsets.UTF_8;
391
392 final long start = Utils.getCurrentTimeMillis();
393 final int statusCode = execute(command, stdout, stderr, cs);
394 final float executionTime = (Utils.getCurrentTimeMillis() - start) / 1000.0f;
395
396 return new WindowsRemoteCommandResult(stdout.toString(), stderr.toString(), executionTime, statusCode);
397 } catch (final WinRMException e) {
398 throw new RuntimeException(e);
399 }
400 },
401 timeout
402 );
403 } catch (final InterruptedException | ExecutionException e) {
404 if (e.getCause() != null) {
405 throw new WinRMException(e.getCause(), e.getCause().getMessage());
406 }
407 throw new WinRMException(e);
408 }
409 }
410
411 @Override
412 public List<Map<String, Object>> executeWql(final String wqlQuery, final long timeout)
413 throws WinRMException, WqlQuerySyntaxException, TimeoutException {
414 Utils.checkNonNull(wqlQuery, "wqlQuery");
415 if (!WmiHelper.isValidWql(wqlQuery)) {
416 throw new WqlQuerySyntaxException(wqlQuery);
417 }
418 Utils.checkArgumentNotZeroOrNegative(timeout, "timeout");
419
420 checkConnectedFirst();
421
422 try {
423 return Utils.execute(
424 () -> {
425 final List<Node> nodes = new ArrayList<>();
426
427 final EnumerateResponse enumerateResponse = enumerate(wqlQuery);
428
429 final boolean endOfSequence = getItemsFrom(enumerateResponse, nodes);
430 if (!endOfSequence) {
431 final String nextContextId = getContextIdFrom(enumerateResponse.getEnumerationContext());
432 pull(nextContextId, nodes);
433 }
434
435 return nodes.stream().map(WinRMService::convertRow).collect(Collectors.toList());
436 },
437 timeout
438 );
439 } catch (final InterruptedException | ExecutionException e) {
440 if (e.getCause() != null) {
441 throw new WinRMException(e.getCause(), e.getCause().getMessage());
442 }
443 throw new WinRMException(e);
444 }
445 }
446
447 public static WinRMInvocationHandler createWinRMInvocationHandlerInstance(
448 final WinRMEndpoint winRMEndpoint,
449 final Bus bus,
450 final long timeout,
451 final String resourceUri,
452 final Path ticketCache,
453 final List<AuthenticationEnum> authentications
454 ) {
455 return new WinRMInvocationHandler(winRMEndpoint, bus, timeout, resourceUri, ticketCache, authentications);
456 }
457
458 private static WinRMWebService createProxyService(final WinRMInvocationHandler winRMInvocationHandler) {
459 return (WinRMWebService) Proxy.newProxyInstance(
460 WinRMWebService.class.getClassLoader(),
461 new Class[] { WinRMWebService.class, BindingProvider.class },
462 winRMInvocationHandler
463 );
464 }
465
466 public EnumerateResponse enumerate(final String wqlQuery) {
467 final FilterType filterType = new FilterType();
468 filterType.setDialect(DIALECT_WQL);
469 filterType.getContent().add(wqlQuery);
470
471 final Enumerate body = new Enumerate();
472 body.setFilter(filterType);
473
474 return wqlWS.enumerate(body);
475 }
476
477 public String pull(final String contextId, final List<Node> nodes) throws WinRMException {
478 final EnumerationContextType enumContext = new EnumerationContextType();
479 enumContext.getContent().add(contextId);
480
481 final Pull body = new Pull();
482 body.setEnumerationContext(enumContext);
483
484 final PullResponse response = wqlWS.pull(body);
485
486 if (response == null) {
487 throw new WinRMException(String.format("Pull failed for context id: %s", contextId));
488 }
489
490 final boolean endOfSequence = getItemsFrom(response, nodes);
491 final String nextContextId = response.getEnumerationContext() == null
492 ? null
493 : getContextIdFrom(response.getEnumerationContext());
494
495 return endOfSequence ? nextContextId : pull(nextContextId, nodes);
496 }
497
498 public ResourceCreated create(final String workingDirectory) {
499 final Shell shell = new Shell();
500 shell.getInputStreams().add("stdin");
501 shell.getOutputStreams().add(STDOUT);
502 shell.getOutputStreams().add(STDERR);
503
504 if (Utils.isNotBlank(workingDirectory)) {
505 shell.setWorkingDirectory(workingDirectory);
506 }
507
508 final ResourceCreated resourceCreated = cmdWS.create(
509 shell,
510 COMMAND_RESOURCE_URI,
511 MAX_ENVELOPE_SIZE,
512 strTimeout,
513 LOCALE,
514 OPTION_SET_CREATE
515 );
516
517 final String shellId = getShellId(resourceCreated);
518
519 shellSelector = new SelectorSetType();
520 final SelectorType selectorType = new SelectorType();
521 selectorType.setName("ShellId");
522 selectorType.getContent().add(shellId);
523 shellSelector.getSelector().add(selectorType);
524
525 return resourceCreated;
526 }
527
528 public int execute(final String command, final Writer out, final Writer err, final Charset charset)
529 throws WinRMException {
530 final CommandLine body = new CommandLine();
531 body.setCommand(command);
532
533 final CommandResponse commandResponse = cmdWS.command(
534 body,
535 COMMAND_RESOURCE_URI,
536 MAX_ENVELOPE_SIZE,
537 strTimeout,
538 LOCALE,
539 shellSelector,
540 OPTION_SET_COMMAND
541 );
542
543 final String commandId = commandResponse.getCommandId();
544
545 try {
546 return receiveCommand(commandId, out, err, charset);
547 } finally {
548 try {
549 final Signal signal = new Signal();
550 signal.setCommandId(commandId);
551 signal.setCode(TERMINATE_CODE);
552
553 cmdWS.signal(signal, COMMAND_RESOURCE_URI, MAX_ENVELOPE_SIZE, strTimeout, LOCALE, shellSelector);
554 } catch (final SOAPFaultException soapFault) {
555 assertFaultCode(soapFault, WSMAN_FAULT_CODE_SHELL_WAS_NOT_FOUND, true);
556 }
557 }
558 }
559
560 private int receiveCommand(final String commandId, final Writer out, final Writer err, final Charset charset)
561 throws WinRMException {
562 while (true) {
563 final DesiredStreamType stream = new DesiredStreamType();
564 stream.setCommandId(commandId);
565 stream.setValue("stdout stderr");
566
567 final Receive receive = new Receive();
568 receive.setDesiredStream(stream);
569
570 try {
571 final ReceiveResponse receiveResponse = cmdWS.receive(
572 receive,
573 COMMAND_RESOURCE_URI,
574 MAX_ENVELOPE_SIZE,
575 strTimeout,
576 LOCALE,
577 shellSelector
578 );
579 getStreams(receiveResponse, out, err, charset);
580
581 final CommandStateType state = receiveResponse.getCommandState();
582 if (COMMAND_STATE_DONE.equals(state.getState())) {
583 return state.getExitCode().intValue();
584 }
585 } catch (final SOAPFaultException soapFault) {
586
587
588 assertFaultCode(soapFault, WSMAN_FAULT_CODE_OPERATION_TIMEOUT_EXPIRED, false);
589 }
590 }
591 }
592
593 private static Map<String, Object> convertRow(final Node node) {
594 return IntStream
595 .range(0, node.getChildNodes().getLength())
596 .mapToObj(node.getChildNodes()::item)
597 .filter(Objects::nonNull)
598 .collect(HashMap::new, (map, child) -> map.put(child.getLocalName(), child.getTextContent()), HashMap::putAll);
599 }
600
601 private static String getShellId(final ResourceCreated resourceCreated) {
602 final XPath xpath = XPathFactory.newInstance().newXPath();
603
604 for (final Element element : resourceCreated.getAny()) {
605 try {
606 final String shellId = xpath.evaluate("//*[local-name()='Selector' and @Name='ShellId']", element);
607 if (shellId != null && !shellId.isEmpty()) {
608 return shellId;
609 }
610 } catch (final XPathExpressionException e) {
611 throw new IllegalStateException(e);
612 }
613 }
614 throw new IllegalStateException("Shell ID not fount in " + resourceCreated);
615 }
616
617 private static void assertFaultCode(final SOAPFaultException soapFault, final String code, final boolean retry) {
618 try {
619 final NodeList faultDetails = soapFault.getFault().getDetail().getChildNodes();
620
621 for (int i = 0; i < faultDetails.getLength(); i++) {
622 final Node item = faultDetails.item(i);
623
624 if ("WSManFault".equals(item.getLocalName())) {
625 if (retry && code.equals(item.getAttributes().getNamedItem("Code").getNodeValue())) {
626 return;
627 }
628 throw soapFault;
629 }
630 }
631 throw soapFault;
632 } catch (final NullPointerException e) {
633 throw soapFault;
634 }
635 }
636
637 private void getStreams(
638 final ReceiveResponse receiveResponse,
639 final Writer out,
640 final Writer err,
641 final Charset charset
642 ) throws WinRMException {
643 final List<StreamType> streams = receiveResponse.getStream();
644 for (final StreamType streamType : streams) {
645 final byte[] value = streamType.getValue();
646 if (value == null) {
647 continue;
648 }
649
650 writeStd(out, STDOUT, streamType, value, charset);
651 writeStd(err, STDERR, streamType, value, charset);
652 }
653 }
654
655 private void writeStd(
656 final Writer std,
657 final String name,
658 final StreamType streamType,
659 final byte[] value,
660 final Charset charset
661 ) throws WinRMException {
662 if (std == null || !name.equals(streamType.getName())) {
663 return;
664 }
665
666 try {
667 if (value.length > 0) {
668 std.write(new String(value, charset));
669 std.flush();
670 }
671
672 if (streamType.isEnd() != null && streamType.isEnd().booleanValue()) {
673 std.close();
674 }
675 } catch (final IOException e) {
676 throw new WinRMException(e);
677 }
678 }
679
680
681
682
683
684
685 public boolean getItemsFrom(final EnumerateResponse response, final List<Node> items) throws WinRMException {
686 for (final Object object : response.getAny()) {
687 if (object instanceof JAXBElement) {
688 final JAXBElement<?> jaxbElement = (JAXBElement<?>) object;
689
690 if (WSEN_ITEMS_QNAME.equals(jaxbElement.getName()) || WSMAN_ITEMS_QNAME.equals(jaxbElement.getName())) {
691 if (jaxbElement.isNil()) {
692
693 } else if (jaxbElement.getValue() instanceof AnyListType) {
694
695 final AnyListType itemList = (AnyListType) jaxbElement.getValue();
696 for (final Object item : itemList.getAny()) {
697 final Node node = toNode(item)
698 .orElseThrow(() ->
699 new WinRMException(
700 "Unsupported element of type %s in EnumerateResponse: %s",
701 object.getClass(),
702 object
703 )
704 );
705
706 items.add(node);
707 }
708 } else {
709 throw new WinRMException(
710 "Unsupported value in EnumerateResponse Items: %s of type: %s",
711 jaxbElement.getValue(),
712 jaxbElement.getValue().getClass()
713 );
714 }
715 } else if (
716 WSEN_END_OF_SEQUENCE_QNAME.equals(jaxbElement.getName()) ||
717 WSMAN_END_OF_SEQUENCE_QNAME.equals(jaxbElement.getName())
718 ) {
719 return true;
720 } else {
721 throw new WinRMException(
722 "Unsupported element in EnumerateResponse: %s with name: %s",
723 jaxbElement,
724 jaxbElement.getName()
725 );
726 }
727 } else if (object instanceof Node) {
728 final Node node = (Node) object;
729
730 if (
731 (WSEN_END_OF_SEQUENCE_QNAME.getNamespaceURI().equals(node.getNamespaceURI()) &&
732 WSEN_END_OF_SEQUENCE_QNAME.getLocalPart().equals(node.getLocalName())) ||
733 (WSMAN_END_OF_SEQUENCE_QNAME.getNamespaceURI().equals(node.getNamespaceURI()) &&
734 WSMAN_END_OF_SEQUENCE_QNAME.getLocalPart().equals(node.getLocalName()))
735 ) {
736 return true;
737 }
738 throw new WinRMException(
739 "Unsupported node in EnumerateResponse: %s with namespace: %s",
740 node.toString(),
741 node.getNamespaceURI()
742 );
743 } else {
744 throw new WinRMException(
745 "Unsupported element in EnumerateResponse: %s, with type: %s",
746 object,
747 object != null ? object.getClass() : null
748 );
749 }
750 }
751
752 return false;
753 }
754
755 private static boolean getItemsFrom(final PullResponse response, final List<Node> items) throws WinRMException {
756 for (final Object item : response.getItems().getAny()) {
757 final Node node = toNode(item)
758 .orElseThrow(() ->
759 new WinRMException(
760 "The pull response contains an unsupported item %s of type %s",
761 item,
762 item != null ? item.getClass() : null
763 )
764 );
765
766 items.add(node);
767 }
768 return response.getEndOfSequence() != null;
769 }
770
771 private static Optional<Node> toNode(final Object item) throws WinRMException {
772 if (item instanceof Node) {
773 return Optional.of((Node) item);
774 }
775
776 if (item instanceof JAXBElement) {
777 final JAXBElement<?> nestedElement = (JAXBElement<?>) item;
778 if (
779 WSMAN_XML_FRAGMENT_QNAME.equals(nestedElement.getName()) &&
780 !nestedElement.isNil() &&
781 nestedElement.getValue() instanceof MixedDataType
782 ) {
783
784 final Document document = createNewDocument();
785 final Element rootElement = document.createElementNS(
786 WSMAN_XML_FRAGMENT_QNAME.getNamespaceURI(),
787 WSMAN_XML_FRAGMENT_QNAME.getLocalPart()
788 );
789 document.appendChild(rootElement);
790
791 final MixedDataType mixed = (MixedDataType) nestedElement.getValue();
792 for (final Object nestedItem : mixed.getContent()) {
793 if (nestedItem instanceof String) {
794
795 } else if (nestedItem instanceof Node) {
796
797 final Node nestedNode = document.importNode((Node) nestedItem, true);
798 rootElement.appendChild(nestedNode);
799 } else {
800 throw new WinRMException(
801 "Unsupported element of type %s in XmlFragment: %s",
802 nestedItem.getClass(),
803 nestedItem
804 );
805 }
806 }
807 return Optional.of(rootElement);
808 }
809 }
810 return Optional.empty();
811 }
812
813 private static Document createNewDocument() throws WinRMException {
814
815
816 synchronized (DOCUMENT_BUILDER_FACTORY) {
817 try {
818 return DOCUMENT_BUILDER_FACTORY.newDocumentBuilder().newDocument();
819 } catch (final ParserConfigurationException e) {
820 throw new WinRMException(e);
821 }
822 }
823 }
824
825 public String getContextIdFrom(final EnumerationContextType context) throws WinRMException {
826
827 if (context == null || context.getContent() == null) {
828 throw new WinRMException("EnumerationContext %s has no content.", context);
829 }
830
831 if (context.getContent().isEmpty()) {
832
833
834 return null;
835 }
836
837 if (context.getContent().size() == 1) {
838 final Object content = context.getContent().get(0);
839 if (content instanceof String) {
840 return (String) content;
841 }
842 throw new WinRMException("Unsupported EnumerationContext content: %s", content);
843 }
844
845 throw new WinRMException(
846 "EnumerationContext contains too many elements, expected: 1 actual: %d",
847 context.getContent().size()
848 );
849 }
850
851 public SelectorSetType getShellSelector() {
852 return shellSelector;
853 }
854
855 @Override
856 public String getHostname() {
857 return winRMEndpoint.getHostname();
858 }
859
860 @Override
861 public String getUsername() {
862 return winRMEndpoint.getRawUsername();
863 }
864
865 @Override
866 public char[] getPassword() {
867 return winRMEndpoint.getPassword();
868 }
869 }