Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,60 +22,35 @@

import java.nio.file.Path;
import java.util.List;
import java.util.Locale;
import org.metricshub.winrm.WindowsRemoteExecutor;
import org.metricshub.winrm.exceptions.WinRMException;
import org.metricshub.winrm.light.LightWinRMService;
import org.metricshub.winrm.service.client.auth.AuthenticationEnum;

/**
* Creates the {@link WindowsRemoteExecutor} that fulfils a request. Since 2.0.0 the dependency-free
* {@link LightWinRMService} is the only backend: the legacy CXF-based backend has been removed.
* The {@value #BACKEND_PROPERTY} system property is kept so operators who still set it get a clear
* error ({@code cxf}) or a no-op ({@code light}) instead of a silent behavior change.
* {@link LightWinRMService} is the only implementation: the legacy CXF-based backend has been removed.
*/
public final class WinRMExecutorFactory {

/** System property selecting the backend; {@code light} is the only supported value. */
public static final String BACKEND_PROPERTY = "org.metricshub.winrm.backend";

private static final String LIGHT = "light";
private static final String CXF = "cxf";

private WinRMExecutorFactory() {}

/**
* Create a {@link WindowsRemoteExecutor} (light backend).
* Create a {@link WindowsRemoteExecutor}.
*
* @param winRMEndpoint endpoint with credentials (mandatory)
* @param timeout timeout in milliseconds (must be > 0)
* @param ticketCache Kerberos ticket cache path (may be {@code null})
* @param authentications requested authentication schemes (may be {@code null})
* @return a light-backed executor
* @throws WinRMException for any problem creating the executor, or when {@value #BACKEND_PROPERTY}
* requests the removed CXF backend or an unknown value
* @return an executor backed by {@link LightWinRMService}
* @throws WinRMException for any problem creating the executor
*/
public static WindowsRemoteExecutor createInstance(
final WinRMEndpoint winRMEndpoint,
final long timeout,
final Path ticketCache,
final List<AuthenticationEnum> authentications
) throws WinRMException {
final String backend = System.getProperty(BACKEND_PROPERTY, LIGHT).trim().toLowerCase(Locale.ROOT);
if (LIGHT.equals(backend)) {
return LightWinRMService.createInstance(winRMEndpoint, timeout, ticketCache, authentications);
}
if (CXF.equals(backend)) {
// Fail loudly: an operator who explicitly pinned the legacy backend must not be silently
// switched to another implementation.
throw new WinRMException(
"The CXF WinRM backend was removed in winrm-java 2.0.0; remove the " +
BACKEND_PROPERTY +
" system property to use the light backend (or stay on winrm-java 1.x)."
);
}
throw new WinRMException(
"Unsupported value \"" + backend + "\" for system property " + BACKEND_PROPERTY + "; expected \"" + LIGHT + "\"."
);
return LightWinRMService.createInstance(winRMEndpoint, timeout, ticketCache, authentications);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,24 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.List;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.metricshub.winrm.WinRMHttpProtocolEnum;
import org.metricshub.winrm.WindowsRemoteExecutor;
import org.metricshub.winrm.exceptions.WinRMException;
import org.metricshub.winrm.light.LightWinRMService;
import org.metricshub.winrm.service.client.auth.AuthenticationEnum;

/** Verifies backend selection and the light backend's capability guards (no network required). */
/** Verifies executor creation and the client's capability guards (no network required). */
class WinRMExecutorFactoryTest {

private static WinRMEndpoint endpoint(final WinRMHttpProtocolEnum protocol) {
return new WinRMEndpoint(protocol, "testhost", null, "user", "pwd".toCharArray(), null);
}

@AfterEach
void clearBackend() {
System.clearProperty(WinRMExecutorFactory.BACKEND_PROPERTY);
}

@Test
void lightBackendSelectedForHttpNtlm() throws Exception {
System.setProperty(WinRMExecutorFactory.BACKEND_PROPERTY, "light");
void createsLightExecutorOverHttp() throws Exception {
try (
final WindowsRemoteExecutor executor = WinRMExecutorFactory.createInstance(
endpoint(WinRMHttpProtocolEnum.HTTP),
Expand All @@ -42,55 +34,9 @@ void lightBackendSelectedForHttpNtlm() throws Exception {
}

@Test
void defaultBackendIsLight() throws Exception {
// No backend property set -> the dependency-free light backend. Building the client does not
// open a connection (that happens on the first operation), so this stays offline.
try (
final WindowsRemoteExecutor executor = WinRMExecutorFactory.createInstance(
endpoint(WinRMHttpProtocolEnum.HTTP),
30000L,
null,
List.of(AuthenticationEnum.NTLM)
)) {
assertInstanceOf(LightWinRMService.class, executor);
}
}

@Test
void cxfBackendRejectedWithRemovalMessage() {
// The CXF backend was removed in 2.0.0. An operator who explicitly pinned it must get a clear
// error, not be silently switched to another implementation.
System.setProperty(WinRMExecutorFactory.BACKEND_PROPERTY, "cxf");
final WinRMException e = assertThrows(
WinRMException.class,
() -> WinRMExecutorFactory.createInstance(
endpoint(WinRMHttpProtocolEnum.HTTP),
30000L,
null,
List.of(AuthenticationEnum.NTLM)
)
);
assertTrue(e.getMessage().contains("removed in winrm-java 2.0.0"), e.getMessage());
}

@Test
void defaultBackendAcceptsHttps() throws Exception {
// Light now supports HTTPS (TLS + plaintext SOAP), so the default backend accepts it. Building
// the client does not open a connection (or a TLS handshake), so this stays offline.
try (
final WindowsRemoteExecutor executor = WinRMExecutorFactory.createInstance(
endpoint(WinRMHttpProtocolEnum.HTTPS),
30000L,
null,
List.of(AuthenticationEnum.NTLM)
)) {
assertInstanceOf(LightWinRMService.class, executor);
}
}

@Test
void lightBackendAcceptsHttps() throws Exception {
System.setProperty(WinRMExecutorFactory.BACKEND_PROPERTY, "light");
void createsLightExecutorOverHttps() throws Exception {
// The client supports HTTPS (TLS + plaintext SOAP). Building it does not open a connection
// (or a TLS handshake), so this stays offline.
try (
final WindowsRemoteExecutor executor = WinRMExecutorFactory.createInstance(
endpoint(WinRMHttpProtocolEnum.HTTPS),
Expand All @@ -106,7 +52,6 @@ void lightBackendAcceptsHttps() throws Exception {
void kerberosOnlyOverHttpRejected() {
// Kerberos requires HTTPS (no message encryption over plain HTTP, matching CXF) and there is no
// other scheme to fall back to, so a Kerberos-only request over HTTP is rejected.
System.setProperty(WinRMExecutorFactory.BACKEND_PROPERTY, "light");
assertThrows(
WinRMException.class,
() -> WinRMExecutorFactory.createInstance(
Expand All @@ -122,7 +67,6 @@ void kerberosOnlyOverHttpRejected() {
void mixedKerberosNtlmFallsBackToNtlmOverHttp() throws Exception {
// Ordered fallback: [KERBEROS, NTLM] over HTTP cannot use Kerberos (HTTPS-only), so it falls back
// to NTLM and constructs successfully. Building the client opens no connection, so this is offline.
System.setProperty(WinRMExecutorFactory.BACKEND_PROPERTY, "light");
try (
final WindowsRemoteExecutor executor = WinRMExecutorFactory.createInstance(
endpoint(WinRMHttpProtocolEnum.HTTP),
Expand All @@ -138,7 +82,6 @@ void mixedKerberosNtlmFallsBackToNtlmOverHttp() throws Exception {
void kerberosOverHttpsAccepted() throws Exception {
// Kerberos is supported over HTTPS; the login/handshake happen on the first operation, so
// constructing the executor stays offline.
System.setProperty(WinRMExecutorFactory.BACKEND_PROPERTY, "light");
try (
final WindowsRemoteExecutor executor = WinRMExecutorFactory.createInstance(
endpoint(WinRMHttpProtocolEnum.HTTPS),
Expand All @@ -150,27 +93,10 @@ void kerberosOverHttpsAccepted() throws Exception {
}
}

@Test
void unsupportedBackendValueRejected() {
// A typo or unknown value must fail loudly instead of silently falling through to a backend the
// operator did not request.
System.setProperty(WinRMExecutorFactory.BACKEND_PROPERTY, "cxff");
assertThrows(
WinRMException.class,
() -> WinRMExecutorFactory.createInstance(
endpoint(WinRMHttpProtocolEnum.HTTP),
30000L,
null,
List.of(AuthenticationEnum.NTLM)
)
);
}

@Test
void closedLightExecutorRejectsOperations() throws Exception {
// close() must release the executor for good: a later operation is rejected, not silently served
// by a fresh reconnect/handshake.
System.setProperty(WinRMExecutorFactory.BACKEND_PROPERTY, "light");
final WindowsRemoteExecutor executor = WinRMExecutorFactory.createInstance(
endpoint(WinRMHttpProtocolEnum.HTTP),
30000L,
Expand Down
Loading