From 5ad705fb86693223d8e06dd55aba1b11c0ee0a2d Mon Sep 17 00:00:00 2001 From: jansupol Date: Wed, 13 Dec 2023 14:11:54 +0100 Subject: [PATCH 1/8] Better Support deployment handshake statuses != 101 Introduce TyrusClientEndpointConfigurator class making UpgradeRequest available before the handshake, not only after it Signed-off-by: jansupol --- .../glassfish/tyrus/client/ClientManager.java | 72 ++------ .../tyrus/client/TyrusClientEngine.java | 18 +- .../DeploymentHandshakeException.java | 44 +++++ .../tyrus/client/exception/Exceptions.java | 43 +++++ .../tyrus/client/exception/package-info.java | 20 +++ .../grizzly/client/GrizzlyClientSocket.java | 13 +- .../jdk/client/JdkClientContainer.java | 7 +- .../tyrus/core/collection/SupplierWithEx.java | 22 +++ .../spi/TyrusClientEndpointConfigurator.java | 42 +++++ .../test/standard_config/HandshakeTest.java | 159 ++++++++++++++++++ 10 files changed, 369 insertions(+), 71 deletions(-) create mode 100644 client/src/main/java/org/glassfish/tyrus/client/exception/DeploymentHandshakeException.java create mode 100644 client/src/main/java/org/glassfish/tyrus/client/exception/Exceptions.java create mode 100644 client/src/main/java/org/glassfish/tyrus/client/exception/package-info.java create mode 100644 core/src/main/java/org/glassfish/tyrus/core/collection/SupplierWithEx.java create mode 100644 spi/src/main/java/org/glassfish/tyrus/spi/TyrusClientEndpointConfigurator.java diff --git a/client/src/main/java/org/glassfish/tyrus/client/ClientManager.java b/client/src/main/java/org/glassfish/tyrus/client/ClientManager.java index 4201a0db..6469f742 100755 --- a/client/src/main/java/org/glassfish/tyrus/client/ClientManager.java +++ b/client/src/main/java/org/glassfish/tyrus/client/ClientManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -42,6 +42,7 @@ import javax.websocket.Session; import javax.websocket.WebSocketContainer; +import org.glassfish.tyrus.client.exception.Exceptions; import org.glassfish.tyrus.core.AnnotatedEndpoint; import org.glassfish.tyrus.core.BaseContainer; import org.glassfish.tyrus.core.ComponentProviderService; @@ -52,6 +53,7 @@ import org.glassfish.tyrus.core.TyrusFuture; import org.glassfish.tyrus.core.TyrusSession; import org.glassfish.tyrus.core.Utils; +import org.glassfish.tyrus.core.collection.SupplierWithEx; import org.glassfish.tyrus.core.monitoring.EndpointEventListener; import org.glassfish.tyrus.spi.ClientContainer; import org.glassfish.tyrus.spi.ClientEngine; @@ -295,74 +297,38 @@ public Session connectToServer(Class annotatedEndpointClass, URI path) throws De "Class argument in connectToServer(Class, URI) is to be annotated endpoint class. Class " + "%s does not have @ClientEndpoint", annotatedEndpointClass.getName())); } - try { - return connectToServer(annotatedEndpointClass, null, path.toString(), true).get(); - } catch (InterruptedException e) { - throw new DeploymentException(e.getMessage(), e); - } catch (ExecutionException e) { - final Throwable cause = e.getCause(); - if (cause instanceof DeploymentException) { - throw (DeploymentException) cause; - } else if (cause instanceof IOException) { - throw (IOException) cause; - } else { - throw new DeploymentException(cause.getMessage(), cause); - } - } + return tryCatchInterruptedExecutionEx(() -> connectToServer(annotatedEndpointClass, null, path.toString(), true)); } @Override public Session connectToServer(Class endpointClass, ClientEndpointConfig cec, URI path) throws DeploymentException, IOException { - try { - return connectToServer(endpointClass, cec, path.toString(), true).get(); - } catch (InterruptedException e) { - throw new DeploymentException(e.getMessage(), e); - } catch (ExecutionException e) { - final Throwable cause = e.getCause(); - if (cause instanceof DeploymentException) { - throw (DeploymentException) cause; - } else if (cause instanceof IOException) { - throw (IOException) cause; - } else { - throw new DeploymentException(cause.getMessage(), cause); - } - } + return tryCatchInterruptedExecutionEx(() -> connectToServer(endpointClass, cec, path.toString(), true)); } @Override public Session connectToServer(Endpoint endpointInstance, ClientEndpointConfig cec, URI path) throws DeploymentException, IOException { - try { - return connectToServer(endpointInstance, cec, path.toString(), true).get(); - } catch (InterruptedException e) { - throw new DeploymentException(e.getMessage(), e); - } catch (ExecutionException e) { - final Throwable cause = e.getCause(); - if (cause instanceof DeploymentException) { - throw (DeploymentException) cause; - } else if (cause instanceof IOException) { - throw (IOException) cause; - } else { - throw new DeploymentException(cause.getMessage(), cause); - } - } + return tryCatchInterruptedExecutionEx(() -> connectToServer(endpointInstance, cec, path.toString(), true)); } @Override public Session connectToServer(Object obj, URI path) throws DeploymentException, IOException { + return tryCatchInterruptedExecutionEx(() -> connectToServer(obj, null, path.toString(), true)); + } + + private Session tryCatchInterruptedExecutionEx(SupplierWithEx, DeploymentException> supplier) + throws DeploymentException, IOException { try { - return connectToServer(obj, null, path.toString(), true).get(); + return supplier.get().get(); } catch (InterruptedException e) { throw new DeploymentException(e.getMessage(), e); } catch (ExecutionException e) { final Throwable cause = e.getCause(); - if (cause instanceof DeploymentException) { - throw (DeploymentException) cause; - } else if (cause instanceof IOException) { + if (cause instanceof IOException) { throw (IOException) cause; } else { - throw new DeploymentException(cause.getMessage(), cause); + throw Exceptions.deploymentException(cause.getMessage(), cause); } } } @@ -652,11 +618,7 @@ public void onClose(TyrusSession session, if (countedDown) { final Throwable exception = listener.getThrowable(); if (exception != null) { - if (exception instanceof DeploymentException) { - throw (DeploymentException) exception; - } else { - throw new DeploymentException("Handshake error.", exception); - } + throw Exceptions.deploymentException("Handshake error.", exception); } future.setResult(listener.getSession()); @@ -669,10 +631,8 @@ public void onClose(TyrusSession session, timeoutHandler.handleTimeout(); } } - } catch (DeploymentException e) { - throw e; } catch (Exception e) { - throw new DeploymentException("Handshake response not received.", e); + throw Exceptions.deploymentException("Handshake response not received.", e); } throw new DeploymentException("Handshake response not received."); diff --git a/client/src/main/java/org/glassfish/tyrus/client/TyrusClientEngine.java b/client/src/main/java/org/glassfish/tyrus/client/TyrusClientEngine.java index 1625a535..3312cecc 100644 --- a/client/src/main/java/org/glassfish/tyrus/client/TyrusClientEngine.java +++ b/client/src/main/java/org/glassfish/tyrus/client/TyrusClientEngine.java @@ -64,6 +64,7 @@ import org.glassfish.tyrus.spi.ClientEngine; import org.glassfish.tyrus.spi.Connection; import org.glassfish.tyrus.spi.ReadHandler; +import org.glassfish.tyrus.spi.TyrusClientEndpointConfigurator; import org.glassfish.tyrus.spi.UpgradeRequest; import org.glassfish.tyrus.spi.UpgradeResponse; import org.glassfish.tyrus.spi.Writer; @@ -168,6 +169,9 @@ public UpgradeRequest createUpgradeRequest(TimeoutHandler timeoutHandler) { clientHandShake.prepareRequest(); UpgradeRequest upgradeRequest = clientHandShake.getRequest(); + if (TyrusClientEndpointConfigurator.class.isInstance(config.getConfigurator())) { + ((TyrusClientEndpointConfigurator) config.getConfigurator()).beforeRequest(upgradeRequest); + } config.getConfigurator().beforeRequest(upgradeRequest.getHeaders()); clientEngineState = TyrusClientEngineState.UPGRADE_REQUEST_CREATED; @@ -250,6 +254,7 @@ public ClientUpgradeInfo processResponse(final UpgradeResponse upgradeResponse, throw new IllegalArgumentException(LocalizationMessages.ARGUMENT_NOT_NULL("upgradeResponse")); } + final ClientEngine.ClientUpgradeInfo upgradeInfo; switch (upgradeResponse.getStatus()) { case 101: return handleSwitchProtocol(upgradeResponse, writer, closeListener); @@ -261,7 +266,8 @@ public ClientUpgradeInfo processResponse(final UpgradeResponse upgradeResponse, case 308: return handleRedirect(upgradeResponse); case 401: - return handleAuth(upgradeResponse); + upgradeInfo = handleAuth(upgradeResponse); + break; case 503: // get Retry-After header @@ -292,19 +298,21 @@ public ClientUpgradeInfo processResponse(final UpgradeResponse upgradeResponse, listener.onError(new RetryAfterException( LocalizationMessages.HANDSHAKE_HTTP_RETRY_AFTER_MESSAGE(), delay)); - return UPGRADE_INFO_FAILED; + upgradeInfo = UPGRADE_INFO_FAILED; + break; default: - ((ClientEndpointConfig) endpointWrapper.getEndpointConfig()).getConfigurator().afterResponse(upgradeResponse); - clientEngineState = TyrusClientEngineState.FAILED; HandshakeException e = new HandshakeException( upgradeResponse.getStatus(), LocalizationMessages.INVALID_RESPONSE_CODE(101, upgradeResponse.getStatus())); listener.onError(e); redirectUriHistory.clear(); - return UPGRADE_INFO_FAILED; + upgradeInfo = UPGRADE_INFO_FAILED; + break; } + ((ClientEndpointConfig) endpointWrapper.getEndpointConfig()).getConfigurator().afterResponse(upgradeResponse); + return upgradeInfo; } redirectUriHistory.clear(); diff --git a/client/src/main/java/org/glassfish/tyrus/client/exception/DeploymentHandshakeException.java b/client/src/main/java/org/glassfish/tyrus/client/exception/DeploymentHandshakeException.java new file mode 100644 index 00000000..d684f968 --- /dev/null +++ b/client/src/main/java/org/glassfish/tyrus/client/exception/DeploymentHandshakeException.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.tyrus.client.exception; + +import org.glassfish.tyrus.core.HandshakeException; + +import javax.websocket.DeploymentException; + +/** + * The {@link DeploymentException} wrapping the {@link HandshakeException} and makes the HTTP status code accessible directly. + */ +public class DeploymentHandshakeException extends DeploymentException { + + public DeploymentHandshakeException(String message) { + super(message); + } + + public DeploymentHandshakeException(String message, HandshakeException cause) { + super(message, cause); + } + + /** + * Get the error code. + * + * @return the error code. + */ + public int getHttpStatusCode() { + return ((HandshakeException) getCause()).getHttpStatusCode(); + } +} diff --git a/client/src/main/java/org/glassfish/tyrus/client/exception/Exceptions.java b/client/src/main/java/org/glassfish/tyrus/client/exception/Exceptions.java new file mode 100644 index 00000000..9c50d491 --- /dev/null +++ b/client/src/main/java/org/glassfish/tyrus/client/exception/Exceptions.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.tyrus.client.exception; + +import org.glassfish.tyrus.core.HandshakeException; + +import javax.websocket.DeploymentException; + +/** + * Converts the exceptions into more specific ones. + */ +public class Exceptions { + + /** + * Get the Deployment Exception, or return the exception if of the type. + * @param message The Exception message + * @param cause The Cause Exception + * @return a Deployment exception. + */ + public static DeploymentException deploymentException(String message, Throwable cause) { + if (DeploymentException.class.isInstance(cause)) { + return (DeploymentException) cause; + } else if (HandshakeException.class.isInstance(cause)) { + return new DeploymentHandshakeException(message, (HandshakeException) cause); + } else { + return new DeploymentException(message, cause); + } + } +} diff --git a/client/src/main/java/org/glassfish/tyrus/client/exception/package-info.java b/client/src/main/java/org/glassfish/tyrus/client/exception/package-info.java new file mode 100644 index 00000000..5f27d2d0 --- /dev/null +++ b/client/src/main/java/org/glassfish/tyrus/client/exception/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +/** + * Common Client Exceptions + */ +package org.glassfish.tyrus.client.exception; diff --git a/containers/grizzly-client/src/main/java/org/glassfish/tyrus/container/grizzly/client/GrizzlyClientSocket.java b/containers/grizzly-client/src/main/java/org/glassfish/tyrus/container/grizzly/client/GrizzlyClientSocket.java index d615d118..25031a63 100644 --- a/containers/grizzly-client/src/main/java/org/glassfish/tyrus/container/grizzly/client/GrizzlyClientSocket.java +++ b/containers/grizzly-client/src/main/java/org/glassfish/tyrus/container/grizzly/client/GrizzlyClientSocket.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -48,6 +48,7 @@ import org.glassfish.tyrus.client.ClientManager; import org.glassfish.tyrus.client.ClientProperties; import org.glassfish.tyrus.client.SslEngineConfigurator; +import org.glassfish.tyrus.client.exception.Exceptions; import org.glassfish.tyrus.core.TyrusFuture; import org.glassfish.tyrus.core.Utils; import org.glassfish.tyrus.spi.ClientEngine; @@ -202,7 +203,7 @@ public class GrizzlyClientSocket { (sharedTransport && sharedTransportTimeoutProperty != null) ? sharedTransportTimeoutProperty : 30; this.clientEngine = clientEngine; } catch (RuntimeException e) { - throw new DeploymentException(e.getMessage(), e); + throw Exceptions.deploymentException(e.getMessage(), e); } grizzlyConnector = new Callable() { @@ -224,12 +225,10 @@ public Void call() throws Exception { public void connect() throws DeploymentException, IOException { try { grizzlyConnector.call(); - } catch (DeploymentException e) { - throw e; } catch (IOException e) { throw e; } catch (Exception e) { - throw new DeploymentException(e.getMessage(), e); + throw Exceptions.deploymentException(e.getMessage(), e); } } @@ -380,7 +379,7 @@ public void handleTimeout() { throw new DeploymentException("SSL handshake has failed", e.getCause()); } catch (Exception e) { closeTransport(privateTransport); - throw new DeploymentException(String.format("Connection to '%s' failed.", requestURI), + throw Exceptions.deploymentException(String.format("Connection to '%s' failed.", requestURI), e.getCause()); } } @@ -410,7 +409,7 @@ public void handleTimeout() { } } - throw new DeploymentException("Connection failed.", exception); + throw Exceptions.deploymentException("Connection failed.", exception); } private static TCPNIOTransport createTransport(ThreadPoolConfig workerThreadPoolConfig, diff --git a/containers/jdk-client/src/main/java/org/glassfish/tyrus/container/jdk/client/JdkClientContainer.java b/containers/jdk-client/src/main/java/org/glassfish/tyrus/container/jdk/client/JdkClientContainer.java index a784ad95..717bbe8b 100644 --- a/containers/jdk-client/src/main/java/org/glassfish/tyrus/container/jdk/client/JdkClientContainer.java +++ b/containers/jdk-client/src/main/java/org/glassfish/tyrus/container/jdk/client/JdkClientContainer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -43,6 +43,7 @@ import org.glassfish.tyrus.client.SslContextConfigurator; import org.glassfish.tyrus.client.SslEngineConfigurator; import org.glassfish.tyrus.client.ThreadPoolConfig; +import org.glassfish.tyrus.client.exception.Exceptions; import org.glassfish.tyrus.core.ReflectionHelper; import org.glassfish.tyrus.core.Utils; import org.glassfish.tyrus.spi.ClientContainer; @@ -184,7 +185,7 @@ public void handleTimeout() { } } - throw new DeploymentException("Connection failed.", exception); + throw Exceptions.deploymentException("Connection failed.", exception); } }; @@ -199,7 +200,7 @@ public void handleTimeout() { throw (IOException) e; } - throw new DeploymentException(e.getMessage(), e); + throw Exceptions.deploymentException(e.getMessage(), e); } } diff --git a/core/src/main/java/org/glassfish/tyrus/core/collection/SupplierWithEx.java b/core/src/main/java/org/glassfish/tyrus/core/collection/SupplierWithEx.java new file mode 100644 index 00000000..2c56fbd4 --- /dev/null +++ b/core/src/main/java/org/glassfish/tyrus/core/collection/SupplierWithEx.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.tyrus.core.collection; + +@FunctionalInterface +public interface SupplierWithEx { + T get() throws EX; +} diff --git a/spi/src/main/java/org/glassfish/tyrus/spi/TyrusClientEndpointConfigurator.java b/spi/src/main/java/org/glassfish/tyrus/spi/TyrusClientEndpointConfigurator.java new file mode 100644 index 00000000..a685d16f --- /dev/null +++ b/spi/src/main/java/org/glassfish/tyrus/spi/TyrusClientEndpointConfigurator.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.tyrus.spi; + +import javax.websocket.ClientEndpointConfig; +import java.util.Map; + +/** + * Extended Configurator that can be used for subclassing the user provided configurator. + * If done so, the additional methods are invoked as described by the methods. + */ +public class TyrusClientEndpointConfigurator extends ClientEndpointConfig.Configurator { + + /** + * This method is called by the implementation after it has formulated the handshake request that will be used + * to initiate the connection to the server, but before {@link #beforeRequest(Map)} is invoked. This allows the + * developer to inspect the handshake request itself prior to the start of the handshake interaction. + *

+ * For modifying the HandshakeRequestHeaders, use {@link #beforeRequest(Map)}. + *

+ * + * @param upgradeRequest the read-only handshake request the implementation is about to send to start the + * handshake interaction. + */ + public void beforeRequest(UpgradeRequest upgradeRequest) { + + } +} diff --git a/tests/e2e/standard-config/src/test/java/org/glassfish/tyrus/test/standard_config/HandshakeTest.java b/tests/e2e/standard-config/src/test/java/org/glassfish/tyrus/test/standard_config/HandshakeTest.java index f7f4bc33..30c49cc5 100755 --- a/tests/e2e/standard-config/src/test/java/org/glassfish/tyrus/test/standard_config/HandshakeTest.java +++ b/tests/e2e/standard-config/src/test/java/org/glassfish/tyrus/test/standard_config/HandshakeTest.java @@ -25,6 +25,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; +import javax.websocket.ClientEndpoint; import javax.websocket.ClientEndpointConfig; import javax.websocket.DeploymentException; import javax.websocket.EndpointConfig; @@ -36,7 +37,10 @@ import javax.websocket.server.ServerEndpointConfig; import org.glassfish.tyrus.client.ClientManager; +import org.glassfish.tyrus.client.exception.DeploymentHandshakeException; import org.glassfish.tyrus.server.Server; +import org.glassfish.tyrus.spi.TyrusClientEndpointConfigurator; +import org.glassfish.tyrus.spi.UpgradeRequest; import org.glassfish.tyrus.spi.UpgradeResponse; import org.glassfish.tyrus.test.standard_config.bean.TestEndpoint; import org.glassfish.tyrus.test.tools.TestContainer; @@ -192,4 +196,159 @@ public EndpointConfig getEndpointConfig() { server.stop(); } } + + public static class Status401SetterConfiguration extends ServerEndpointConfig.Configurator { + @Override + public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) { + ((UpgradeResponse) response).setStatus(401); + response.getHeaders().put(HEADER, Collections.singletonList(HEADER)); + } + } + + @Test + public void test401InConfigurer() throws DeploymentException, IOException { + @ServerEndpoint(value = "/status", configurator = Status401SetterConfiguration.class) + class Status401SetterEndpoint { + @OnMessage + public void onMessage(String message) { + throw new IllegalStateException("ON MESSAGE"); + } + } + + final AtomicReference status = new AtomicReference<>(); + final AtomicReference header = new AtomicReference<>(); + + Server server = startServer(Status401SetterEndpoint.class); + + ClientEndpointConfig.Configurator cecc = new ClientEndpointConfig.Configurator() { + @Override + public void afterResponse(HandshakeResponse hr) { + status.set(((UpgradeResponse) hr).getStatus()); + header.set(((UpgradeResponse) hr).getFirstHeaderValue(HEADER)); + } + }; + + ClientEndpointConfig cec = ClientEndpointConfig.Builder.create().configurator(cecc).build(); + + try { + ClientManager client = createClient(); + Session session = client.connectToServer(new TestEndpointAdapter() { + @Override + public void onMessage(String message) { + } + + @Override + public void onOpen(Session session) { + try { + session.getBasicRemote().sendText("This should never be sent"); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public EndpointConfig getEndpointConfig() { + return cec; + } + }, cec, getURI(Status401SetterEndpoint.class)); + + throw new IllegalStateException("DeploymentException was not thrown"); + } catch (DeploymentHandshakeException de) { + Assert.assertEquals(401, status.get().intValue()); + Assert.assertEquals(status.get().intValue(), de.getHttpStatusCode()); + Assert.assertEquals(HEADER, header.get()); + } finally { + server.stop(); + } + } + + @Test + public void beforeRequestUpgradeRequest() throws DeploymentException, IOException { + @ServerEndpoint(value = "/status", configurator = StatusSetterConfiguration.class) + class StatusSetterEndpoint { + @OnMessage + public void onMessage(String message) { + throw new IllegalStateException("ON MESSAGE"); + } + } + + final AtomicReference requestAtomicReference = new AtomicReference<>(); + + Server server = startServer(StatusSetterEndpoint.class); + + ClientEndpointConfig.Configurator cecc = new TyrusClientEndpointConfigurator() { + @Override + public void beforeRequest(UpgradeRequest upgradeRequest) { + requestAtomicReference.set(upgradeRequest); + } + }; + + ClientEndpointConfig cec = ClientEndpointConfig.Builder.create().configurator(cecc).build(); + + try { + ClientManager client = createClient(); + Session session = client.connectToServer(new TestEndpointAdapter() { + @Override + public void onMessage(String message) { + } + + @Override + public void onOpen(Session session) { + try { + session.getBasicRemote().sendText("This should never be sent"); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public EndpointConfig getEndpointConfig() { + return cec; + } + }, cec, getURI(StatusSetterEndpoint.class)); + + throw new IllegalStateException("DeploymentException was not thrown"); + } catch (DeploymentException de) { + Assert.assertEquals(getURI(StatusSetterEndpoint.class), requestAtomicReference.get().getRequestURI()); + } finally { + server.stop(); + } + } + + public static final AtomicReference REQUEST_ATOMIC_REFERENCE = new AtomicReference<>(); + + public static class AnnotatedBeforeRequestTestClientConfigurator extends TyrusClientEndpointConfigurator { + @Override + public void beforeRequest(UpgradeRequest upgradeRequest) { + REQUEST_ATOMIC_REFERENCE.set(upgradeRequest); + } + } + + @ClientEndpoint(configurator = AnnotatedBeforeRequestTestClientConfigurator.class) + public static class AnnotatedBeforeRequestTestClient { + + } + + @Test + public void beforeRequestUpgradeRequestOnAnnotatedClient() throws DeploymentException, IOException { + @ServerEndpoint(value = "/status", configurator = StatusSetterConfiguration.class) + class StatusSetterEndpoint { + @OnMessage + public void onMessage(String message) { + throw new IllegalStateException("ON MESSAGE"); + } + } + + Server server = startServer(StatusSetterEndpoint.class); + + try { + ClientManager client = createClient(); + Session session = client.connectToServer(AnnotatedBeforeRequestTestClient.class, getURI(StatusSetterEndpoint.class)); + throw new IllegalStateException("DeploymentException was not thrown"); + } catch (DeploymentException de) { + Assert.assertEquals(getURI(StatusSetterEndpoint.class), REQUEST_ATOMIC_REFERENCE.get().getRequestURI()); + } finally { + server.stop(); + } + } } From ee87803f35b26698a2cbc643687177cf49351846 Mon Sep 17 00:00:00 2001 From: jansupol Date: Tue, 19 Dec 2023 21:15:22 +0100 Subject: [PATCH 2/8] Merge remote-tracking branch 'master' into `2.0.x` Signed-off-by: jansupol --- client/src/main/java/module-info.java | 3 +- .../glassfish/tyrus/client/ClientManager.java | 72 ++------ .../tyrus/client/TyrusClientEngine.java | 18 +- .../DeploymentHandshakeException.java | 44 +++++ .../tyrus/client/exception/Exceptions.java | 43 +++++ .../tyrus/client/exception/package-info.java | 20 +++ .../grizzly/client/GrizzlyClientSocket.java | 13 +- .../jdk/client/JdkClientContainer.java | 7 +- core/src/main/java/module-info.java | 4 +- .../tyrus/core/collection/SupplierWithEx.java | 22 +++ .../spi/TyrusClientEndpointConfigurator.java | 42 +++++ .../test/standard_config/HandshakeTest.java | 159 ++++++++++++++++++ 12 files changed, 374 insertions(+), 73 deletions(-) create mode 100644 client/src/main/java/org/glassfish/tyrus/client/exception/DeploymentHandshakeException.java create mode 100644 client/src/main/java/org/glassfish/tyrus/client/exception/Exceptions.java create mode 100644 client/src/main/java/org/glassfish/tyrus/client/exception/package-info.java create mode 100644 core/src/main/java/org/glassfish/tyrus/core/collection/SupplierWithEx.java create mode 100644 spi/src/main/java/org/glassfish/tyrus/spi/TyrusClientEndpointConfigurator.java diff --git a/client/src/main/java/module-info.java b/client/src/main/java/module-info.java index 1eb023ed..f9dccf9d 100644 --- a/client/src/main/java/module-info.java +++ b/client/src/main/java/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -26,4 +26,5 @@ exports org.glassfish.tyrus.client; exports org.glassfish.tyrus.client.auth; + exports org.glassfish.tyrus.client.exception; } \ No newline at end of file diff --git a/client/src/main/java/org/glassfish/tyrus/client/ClientManager.java b/client/src/main/java/org/glassfish/tyrus/client/ClientManager.java index b854523f..fd68e9e2 100755 --- a/client/src/main/java/org/glassfish/tyrus/client/ClientManager.java +++ b/client/src/main/java/org/glassfish/tyrus/client/ClientManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -42,6 +42,7 @@ import jakarta.websocket.Session; import jakarta.websocket.WebSocketContainer; +import org.glassfish.tyrus.client.exception.Exceptions; import org.glassfish.tyrus.core.AnnotatedEndpoint; import org.glassfish.tyrus.core.BaseContainer; import org.glassfish.tyrus.core.ComponentProviderService; @@ -52,6 +53,7 @@ import org.glassfish.tyrus.core.TyrusFuture; import org.glassfish.tyrus.core.TyrusSession; import org.glassfish.tyrus.core.Utils; +import org.glassfish.tyrus.core.collection.SupplierWithEx; import org.glassfish.tyrus.core.monitoring.EndpointEventListener; import org.glassfish.tyrus.spi.ClientContainer; import org.glassfish.tyrus.spi.ClientEngine; @@ -295,74 +297,38 @@ public Session connectToServer(Class annotatedEndpointClass, URI path) throws De "Class argument in connectToServer(Class, URI) is to be annotated endpoint class. Class " + "%s does not have @ClientEndpoint", annotatedEndpointClass.getName())); } - try { - return connectToServer(annotatedEndpointClass, null, path.toString(), true).get(); - } catch (InterruptedException e) { - throw new DeploymentException(e.getMessage(), e); - } catch (ExecutionException e) { - final Throwable cause = e.getCause(); - if (cause instanceof DeploymentException) { - throw (DeploymentException) cause; - } else if (cause instanceof IOException) { - throw (IOException) cause; - } else { - throw new DeploymentException(cause.getMessage(), cause); - } - } + return tryCatchInterruptedExecutionEx(() -> connectToServer(annotatedEndpointClass, null, path.toString(), true)); } @Override public Session connectToServer(Class endpointClass, ClientEndpointConfig cec, URI path) throws DeploymentException, IOException { - try { - return connectToServer(endpointClass, cec, path.toString(), true).get(); - } catch (InterruptedException e) { - throw new DeploymentException(e.getMessage(), e); - } catch (ExecutionException e) { - final Throwable cause = e.getCause(); - if (cause instanceof DeploymentException) { - throw (DeploymentException) cause; - } else if (cause instanceof IOException) { - throw (IOException) cause; - } else { - throw new DeploymentException(cause.getMessage(), cause); - } - } + return tryCatchInterruptedExecutionEx(() -> connectToServer(endpointClass, cec, path.toString(), true)); } @Override public Session connectToServer(Endpoint endpointInstance, ClientEndpointConfig cec, URI path) throws DeploymentException, IOException { - try { - return connectToServer(endpointInstance, cec, path.toString(), true).get(); - } catch (InterruptedException e) { - throw new DeploymentException(e.getMessage(), e); - } catch (ExecutionException e) { - final Throwable cause = e.getCause(); - if (cause instanceof DeploymentException) { - throw (DeploymentException) cause; - } else if (cause instanceof IOException) { - throw (IOException) cause; - } else { - throw new DeploymentException(cause.getMessage(), cause); - } - } + return tryCatchInterruptedExecutionEx(() -> connectToServer(endpointInstance, cec, path.toString(), true)); } @Override public Session connectToServer(Object obj, URI path) throws DeploymentException, IOException { + return tryCatchInterruptedExecutionEx(() -> connectToServer(obj, null, path.toString(), true)); + } + + private Session tryCatchInterruptedExecutionEx(SupplierWithEx, DeploymentException> supplier) + throws DeploymentException, IOException { try { - return connectToServer(obj, null, path.toString(), true).get(); + return supplier.get().get(); } catch (InterruptedException e) { throw new DeploymentException(e.getMessage(), e); } catch (ExecutionException e) { final Throwable cause = e.getCause(); - if (cause instanceof DeploymentException) { - throw (DeploymentException) cause; - } else if (cause instanceof IOException) { + if (cause instanceof IOException) { throw (IOException) cause; } else { - throw new DeploymentException(cause.getMessage(), cause); + throw Exceptions.deploymentException(cause.getMessage(), cause); } } } @@ -652,11 +618,7 @@ public void onClose(TyrusSession session, if (countedDown) { final Throwable exception = listener.getThrowable(); if (exception != null) { - if (exception instanceof DeploymentException) { - throw (DeploymentException) exception; - } else { - throw new DeploymentException("Handshake error.", exception); - } + throw Exceptions.deploymentException("Handshake error.", exception); } future.setResult(listener.getSession()); @@ -669,10 +631,8 @@ public void onClose(TyrusSession session, timeoutHandler.handleTimeout(); } } - } catch (DeploymentException e) { - throw e; } catch (Exception e) { - throw new DeploymentException("Handshake response not received.", e); + throw Exceptions.deploymentException("Handshake response not received.", e); } throw new DeploymentException("Handshake response not received."); diff --git a/client/src/main/java/org/glassfish/tyrus/client/TyrusClientEngine.java b/client/src/main/java/org/glassfish/tyrus/client/TyrusClientEngine.java index 9af08475..d4cd1bf2 100644 --- a/client/src/main/java/org/glassfish/tyrus/client/TyrusClientEngine.java +++ b/client/src/main/java/org/glassfish/tyrus/client/TyrusClientEngine.java @@ -65,6 +65,7 @@ import org.glassfish.tyrus.spi.ClientEngine; import org.glassfish.tyrus.spi.Connection; import org.glassfish.tyrus.spi.ReadHandler; +import org.glassfish.tyrus.spi.TyrusClientEndpointConfigurator; import org.glassfish.tyrus.spi.UpgradeRequest; import org.glassfish.tyrus.spi.UpgradeResponse; import org.glassfish.tyrus.spi.Writer; @@ -169,6 +170,9 @@ public UpgradeRequest createUpgradeRequest(TimeoutHandler timeoutHandler) { clientHandShake.prepareRequest(); UpgradeRequest upgradeRequest = clientHandShake.getRequest(); + if (TyrusClientEndpointConfigurator.class.isInstance(config.getConfigurator())) { + ((TyrusClientEndpointConfigurator) config.getConfigurator()).beforeRequest(upgradeRequest); + } config.getConfigurator().beforeRequest(upgradeRequest.getHeaders()); clientEngineState = TyrusClientEngineState.UPGRADE_REQUEST_CREATED; @@ -251,6 +255,7 @@ public ClientUpgradeInfo processResponse(final UpgradeResponse upgradeResponse, throw new IllegalArgumentException(LocalizationMessages.ARGUMENT_NOT_NULL("upgradeResponse")); } + final ClientEngine.ClientUpgradeInfo upgradeInfo; switch (upgradeResponse.getStatus()) { case 101: return handleSwitchProtocol(upgradeResponse, writer, closeListener); @@ -262,7 +267,8 @@ public ClientUpgradeInfo processResponse(final UpgradeResponse upgradeResponse, case 308: return handleRedirect(upgradeResponse); case 401: - return handleAuth(upgradeResponse); + upgradeInfo = handleAuth(upgradeResponse); + break; case 503: // get Retry-After header @@ -293,19 +299,21 @@ public ClientUpgradeInfo processResponse(final UpgradeResponse upgradeResponse, listener.onError(new RetryAfterException( LocalizationMessages.HANDSHAKE_HTTP_RETRY_AFTER_MESSAGE(), delay)); - return UPGRADE_INFO_FAILED; + upgradeInfo = UPGRADE_INFO_FAILED; + break; default: - ((ClientEndpointConfig) endpointWrapper.getEndpointConfig()).getConfigurator().afterResponse(upgradeResponse); - clientEngineState = TyrusClientEngineState.FAILED; HandshakeException e = new HandshakeException( upgradeResponse.getStatus(), LocalizationMessages.INVALID_RESPONSE_CODE(101, upgradeResponse.getStatus())); listener.onError(e); redirectUriHistory.clear(); - return UPGRADE_INFO_FAILED; + upgradeInfo = UPGRADE_INFO_FAILED; + break; } + ((ClientEndpointConfig) endpointWrapper.getEndpointConfig()).getConfigurator().afterResponse(upgradeResponse); + return upgradeInfo; } redirectUriHistory.clear(); diff --git a/client/src/main/java/org/glassfish/tyrus/client/exception/DeploymentHandshakeException.java b/client/src/main/java/org/glassfish/tyrus/client/exception/DeploymentHandshakeException.java new file mode 100644 index 00000000..ec1d2977 --- /dev/null +++ b/client/src/main/java/org/glassfish/tyrus/client/exception/DeploymentHandshakeException.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.tyrus.client.exception; + +import org.glassfish.tyrus.core.HandshakeException; + +import jakarta.websocket.DeploymentException; + +/** + * The {@link DeploymentException} wrapping the {@link HandshakeException} and makes the HTTP status code accessible directly. + */ +public class DeploymentHandshakeException extends DeploymentException { + + public DeploymentHandshakeException(String message) { + super(message); + } + + public DeploymentHandshakeException(String message, HandshakeException cause) { + super(message, cause); + } + + /** + * Get the error code. + * + * @return the error code. + */ + public int getHttpStatusCode() { + return ((HandshakeException) getCause()).getHttpStatusCode(); + } +} diff --git a/client/src/main/java/org/glassfish/tyrus/client/exception/Exceptions.java b/client/src/main/java/org/glassfish/tyrus/client/exception/Exceptions.java new file mode 100644 index 00000000..4b6c1c12 --- /dev/null +++ b/client/src/main/java/org/glassfish/tyrus/client/exception/Exceptions.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.tyrus.client.exception; + +import org.glassfish.tyrus.core.HandshakeException; + +import jakarta.websocket.DeploymentException; + +/** + * Converts the exceptions into more specific ones. + */ +public class Exceptions { + + /** + * Get the Deployment Exception, or return the exception if of the type. + * @param message The Exception message + * @param cause The Cause Exception + * @return a Deployment exception. + */ + public static DeploymentException deploymentException(String message, Throwable cause) { + if (DeploymentException.class.isInstance(cause)) { + return (DeploymentException) cause; + } else if (HandshakeException.class.isInstance(cause)) { + return new DeploymentHandshakeException(message, (HandshakeException) cause); + } else { + return new DeploymentException(message, cause); + } + } +} diff --git a/client/src/main/java/org/glassfish/tyrus/client/exception/package-info.java b/client/src/main/java/org/glassfish/tyrus/client/exception/package-info.java new file mode 100644 index 00000000..5f27d2d0 --- /dev/null +++ b/client/src/main/java/org/glassfish/tyrus/client/exception/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +/** + * Common Client Exceptions + */ +package org.glassfish.tyrus.client.exception; diff --git a/containers/grizzly-client/src/main/java/org/glassfish/tyrus/container/grizzly/client/GrizzlyClientSocket.java b/containers/grizzly-client/src/main/java/org/glassfish/tyrus/container/grizzly/client/GrizzlyClientSocket.java index 0f438f1e..5d679a52 100644 --- a/containers/grizzly-client/src/main/java/org/glassfish/tyrus/container/grizzly/client/GrizzlyClientSocket.java +++ b/containers/grizzly-client/src/main/java/org/glassfish/tyrus/container/grizzly/client/GrizzlyClientSocket.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -48,6 +48,7 @@ import org.glassfish.tyrus.client.ClientManager; import org.glassfish.tyrus.client.ClientProperties; import org.glassfish.tyrus.client.SslEngineConfigurator; +import org.glassfish.tyrus.client.exception.Exceptions; import org.glassfish.tyrus.core.TyrusFuture; import org.glassfish.tyrus.core.Utils; import org.glassfish.tyrus.spi.ClientEngine; @@ -202,7 +203,7 @@ public class GrizzlyClientSocket { (sharedTransport && sharedTransportTimeoutProperty != null) ? sharedTransportTimeoutProperty : 30; this.clientEngine = clientEngine; } catch (RuntimeException e) { - throw new DeploymentException(e.getMessage(), e); + throw Exceptions.deploymentException(e.getMessage(), e); } grizzlyConnector = new Callable() { @@ -224,12 +225,10 @@ public Void call() throws Exception { public void connect() throws DeploymentException, IOException { try { grizzlyConnector.call(); - } catch (DeploymentException e) { - throw e; } catch (IOException e) { throw e; } catch (Exception e) { - throw new DeploymentException(e.getMessage(), e); + throw Exceptions.deploymentException(e.getMessage(), e); } } @@ -380,7 +379,7 @@ public void handleTimeout() { throw new DeploymentException("SSL handshake has failed", e.getCause()); } catch (Exception e) { closeTransport(privateTransport); - throw new DeploymentException(String.format("Connection to '%s' failed.", requestURI), + throw Exceptions.deploymentException(String.format("Connection to '%s' failed.", requestURI), e.getCause()); } } @@ -410,7 +409,7 @@ public void handleTimeout() { } } - throw new DeploymentException("Connection failed.", exception); + throw Exceptions.deploymentException("Connection failed.", exception); } private static TCPNIOTransport createTransport(ThreadPoolConfig workerThreadPoolConfig, diff --git a/containers/jdk-client/src/main/java/org/glassfish/tyrus/container/jdk/client/JdkClientContainer.java b/containers/jdk-client/src/main/java/org/glassfish/tyrus/container/jdk/client/JdkClientContainer.java index 4db81087..c46a6470 100644 --- a/containers/jdk-client/src/main/java/org/glassfish/tyrus/container/jdk/client/JdkClientContainer.java +++ b/containers/jdk-client/src/main/java/org/glassfish/tyrus/container/jdk/client/JdkClientContainer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -43,6 +43,7 @@ import org.glassfish.tyrus.client.SslContextConfigurator; import org.glassfish.tyrus.client.SslEngineConfigurator; import org.glassfish.tyrus.client.ThreadPoolConfig; +import org.glassfish.tyrus.client.exception.Exceptions; import org.glassfish.tyrus.core.ReflectionHelper; import org.glassfish.tyrus.core.Utils; import org.glassfish.tyrus.spi.ClientContainer; @@ -184,7 +185,7 @@ public void handleTimeout() { } } - throw new DeploymentException("Connection failed.", exception); + throw Exceptions.deploymentException("Connection failed.", exception); } }; @@ -199,7 +200,7 @@ public void handleTimeout() { throw (IOException) e; } - throw new DeploymentException(e.getMessage(), e); + throw Exceptions.deploymentException(e.getMessage(), e); } } diff --git a/core/src/main/java/module-info.java b/core/src/main/java/module-info.java index feb3cb3c..590db75d 100644 --- a/core/src/main/java/module-info.java +++ b/core/src/main/java/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -19,6 +19,7 @@ */ module org.glassfish.tyrus.core { requires java.logging; + requires static java.xml; requires static jakarta.xml.bind; requires transitive jakarta.websocket; @@ -30,6 +31,7 @@ exports org.glassfish.tyrus.core; exports org.glassfish.tyrus.core.cluster; exports org.glassfish.tyrus.core.coder; + exports org.glassfish.tyrus.core.collection; exports org.glassfish.tyrus.core.extension; exports org.glassfish.tyrus.core.frame; exports org.glassfish.tyrus.core.l10n; diff --git a/core/src/main/java/org/glassfish/tyrus/core/collection/SupplierWithEx.java b/core/src/main/java/org/glassfish/tyrus/core/collection/SupplierWithEx.java new file mode 100644 index 00000000..2c56fbd4 --- /dev/null +++ b/core/src/main/java/org/glassfish/tyrus/core/collection/SupplierWithEx.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.tyrus.core.collection; + +@FunctionalInterface +public interface SupplierWithEx { + T get() throws EX; +} diff --git a/spi/src/main/java/org/glassfish/tyrus/spi/TyrusClientEndpointConfigurator.java b/spi/src/main/java/org/glassfish/tyrus/spi/TyrusClientEndpointConfigurator.java new file mode 100644 index 00000000..74bcb19c --- /dev/null +++ b/spi/src/main/java/org/glassfish/tyrus/spi/TyrusClientEndpointConfigurator.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.tyrus.spi; + +import jakarta.websocket.ClientEndpointConfig; +import java.util.Map; + +/** + * Extended Configurator that can be used for subclassing the user provided configurator. + * If done so, the additional methods are invoked as described by the methods. + */ +public class TyrusClientEndpointConfigurator extends ClientEndpointConfig.Configurator { + + /** + * This method is called by the implementation after it has formulated the handshake request that will be used + * to initiate the connection to the server, but before {@link #beforeRequest(Map)} is invoked. This allows the + * developer to inspect the handshake request itself prior to the start of the handshake interaction. + *

+ * For modifying the HandshakeRequestHeaders, use {@link #beforeRequest(Map)}. + *

+ * + * @param upgradeRequest the read-only handshake request the implementation is about to send to start the + * handshake interaction. + */ + public void beforeRequest(UpgradeRequest upgradeRequest) { + + } +} diff --git a/tests/e2e/standard-config/src/test/java/org/glassfish/tyrus/test/standard_config/HandshakeTest.java b/tests/e2e/standard-config/src/test/java/org/glassfish/tyrus/test/standard_config/HandshakeTest.java index fc800e83..32dc76b1 100755 --- a/tests/e2e/standard-config/src/test/java/org/glassfish/tyrus/test/standard_config/HandshakeTest.java +++ b/tests/e2e/standard-config/src/test/java/org/glassfish/tyrus/test/standard_config/HandshakeTest.java @@ -25,6 +25,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; +import jakarta.websocket.ClientEndpoint; import jakarta.websocket.ClientEndpointConfig; import jakarta.websocket.DeploymentException; import jakarta.websocket.EndpointConfig; @@ -36,7 +37,10 @@ import jakarta.websocket.server.ServerEndpointConfig; import org.glassfish.tyrus.client.ClientManager; +import org.glassfish.tyrus.client.exception.DeploymentHandshakeException; import org.glassfish.tyrus.server.Server; +import org.glassfish.tyrus.spi.TyrusClientEndpointConfigurator; +import org.glassfish.tyrus.spi.UpgradeRequest; import org.glassfish.tyrus.spi.UpgradeResponse; import org.glassfish.tyrus.test.standard_config.bean.TestEndpoint; import org.glassfish.tyrus.test.tools.TestContainer; @@ -192,4 +196,159 @@ public EndpointConfig getEndpointConfig() { server.stop(); } } + + public static class Status401SetterConfiguration extends ServerEndpointConfig.Configurator { + @Override + public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) { + ((UpgradeResponse) response).setStatus(401); + response.getHeaders().put(HEADER, Collections.singletonList(HEADER)); + } + } + + @Test + public void test401InConfigurer() throws DeploymentException, IOException { + @ServerEndpoint(value = "/status", configurator = Status401SetterConfiguration.class) + class Status401SetterEndpoint { + @OnMessage + public void onMessage(String message) { + throw new IllegalStateException("ON MESSAGE"); + } + } + + final AtomicReference status = new AtomicReference<>(); + final AtomicReference header = new AtomicReference<>(); + + Server server = startServer(Status401SetterEndpoint.class); + + ClientEndpointConfig.Configurator cecc = new ClientEndpointConfig.Configurator() { + @Override + public void afterResponse(HandshakeResponse hr) { + status.set(((UpgradeResponse) hr).getStatus()); + header.set(((UpgradeResponse) hr).getFirstHeaderValue(HEADER)); + } + }; + + ClientEndpointConfig cec = ClientEndpointConfig.Builder.create().configurator(cecc).build(); + + try { + ClientManager client = createClient(); + Session session = client.connectToServer(new TestEndpointAdapter() { + @Override + public void onMessage(String message) { + } + + @Override + public void onOpen(Session session) { + try { + session.getBasicRemote().sendText("This should never be sent"); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public EndpointConfig getEndpointConfig() { + return cec; + } + }, cec, getURI(Status401SetterEndpoint.class)); + + throw new IllegalStateException("DeploymentException was not thrown"); + } catch (DeploymentHandshakeException de) { + Assert.assertEquals(401, status.get().intValue()); + Assert.assertEquals(status.get().intValue(), de.getHttpStatusCode()); + Assert.assertEquals(HEADER, header.get()); + } finally { + server.stop(); + } + } + + @Test + public void beforeRequestUpgradeRequest() throws DeploymentException, IOException { + @ServerEndpoint(value = "/status", configurator = StatusSetterConfiguration.class) + class StatusSetterEndpoint { + @OnMessage + public void onMessage(String message) { + throw new IllegalStateException("ON MESSAGE"); + } + } + + final AtomicReference requestAtomicReference = new AtomicReference<>(); + + Server server = startServer(StatusSetterEndpoint.class); + + ClientEndpointConfig.Configurator cecc = new TyrusClientEndpointConfigurator() { + @Override + public void beforeRequest(UpgradeRequest upgradeRequest) { + requestAtomicReference.set(upgradeRequest); + } + }; + + ClientEndpointConfig cec = ClientEndpointConfig.Builder.create().configurator(cecc).build(); + + try { + ClientManager client = createClient(); + Session session = client.connectToServer(new TestEndpointAdapter() { + @Override + public void onMessage(String message) { + } + + @Override + public void onOpen(Session session) { + try { + session.getBasicRemote().sendText("This should never be sent"); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public EndpointConfig getEndpointConfig() { + return cec; + } + }, cec, getURI(StatusSetterEndpoint.class)); + + throw new IllegalStateException("DeploymentException was not thrown"); + } catch (DeploymentException de) { + Assert.assertEquals(getURI(StatusSetterEndpoint.class), requestAtomicReference.get().getRequestURI()); + } finally { + server.stop(); + } + } + + public static final AtomicReference REQUEST_ATOMIC_REFERENCE = new AtomicReference<>(); + + public static class AnnotatedBeforeRequestTestClientConfigurator extends TyrusClientEndpointConfigurator { + @Override + public void beforeRequest(UpgradeRequest upgradeRequest) { + REQUEST_ATOMIC_REFERENCE.set(upgradeRequest); + } + } + + @ClientEndpoint(configurator = AnnotatedBeforeRequestTestClientConfigurator.class) + public static class AnnotatedBeforeRequestTestClient { + + } + + @Test + public void beforeRequestUpgradeRequestOnAnnotatedClient() throws DeploymentException, IOException { + @ServerEndpoint(value = "/status", configurator = StatusSetterConfiguration.class) + class StatusSetterEndpoint { + @OnMessage + public void onMessage(String message) { + throw new IllegalStateException("ON MESSAGE"); + } + } + + Server server = startServer(StatusSetterEndpoint.class); + + try { + ClientManager client = createClient(); + Session session = client.connectToServer(AnnotatedBeforeRequestTestClient.class, getURI(StatusSetterEndpoint.class)); + throw new IllegalStateException("DeploymentException was not thrown"); + } catch (DeploymentException de) { + Assert.assertEquals(getURI(StatusSetterEndpoint.class), REQUEST_ATOMIC_REFERENCE.get().getRequestURI()); + } finally { + server.stop(); + } + } } From 1a83c0dff16342a245f449f43bb99cdadbba70c7 Mon Sep 17 00:00:00 2001 From: jansupol Date: Wed, 20 Dec 2023 11:59:01 +0100 Subject: [PATCH 3/8] Build Tyrus on JDK 17 Signed-off-by: jansupol --- etc/jenkins/Jenkinsfile_ci_build | 34 ++++++++++++++++---------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/etc/jenkins/Jenkinsfile_ci_build b/etc/jenkins/Jenkinsfile_ci_build index 2ac02633..27c197e8 100644 --- a/etc/jenkins/Jenkinsfile_ci_build +++ b/etc/jenkins/Jenkinsfile_ci_build @@ -22,34 +22,34 @@ pipeline { ''' } } - stage('JDK 13 ') { +// stage('JDK 13 ') { +// agent { +// label 'centos-7' +// } +// tools { +// jdk 'openjdk-jdk13-latest' +// maven 'apache-maven-latest' +// } +// steps { +// sh ''' +// mvn -U -C -Dtyrus.test.container.client=org.glassfish.tyrus.container.grizzly.client.GrizzlyClientContainer -Pbundles clean install -Dmaven.javadoc.skip=true +// ''' +// } +// } + stage('JDK 17 ') { agent { label 'centos-7' } tools { - jdk 'openjdk-jdk13-latest' + jdk 'openjdk-jdk17-latest' maven 'apache-maven-latest' } steps { sh ''' - mvn -U -C -Dtyrus.test.container.client=org.glassfish.tyrus.container.grizzly.client.GrizzlyClientContainer -Pbundles clean install -Dmaven.javadoc.skip=true + mvn -U -C -Dtyrus.test.container.client=org.glassfish.tyrus.container.grizzly.client.GrizzlyClientContainer -Pbundles clean install -Dmaven.javadoc.skip=true ''' } } - //stage('JDK 17 ') { - // agent { - // label 'centos-7' - // } - // tools { - // jdk 'openjdk-jdk17-latest' - // maven 'apache-maven-latest' - // } - // steps { - // sh ''' - // mvn -U -C -Dtyrus.test.container.client=org.glassfish.tyrus.container.grizzly.client.GrizzlyClientContainer -Pbundles clean install -Dmaven.javadoc.skip=true - // ''' - // } - //} } } } From 3a3cbb2d3d98dfc06ab16e720dea27163cc0fc75 Mon Sep 17 00:00:00 2001 From: jansupol Date: Tue, 19 Dec 2023 21:28:13 +0100 Subject: [PATCH 4/8] Support JDK 7 Signed-off-by: jansupol --- .../glassfish/tyrus/client/ClientManager.java | 39 ++++++++++++++----- .../tyrus/core/AnnotatedEndpoint.java | 6 +-- .../core/TyrusServerEndpointConfigurator.java | 10 ++++- ext/client-java8/pom.xml | 9 ++++- pom.xml | 6 ++- tests/release-test/pom.xml | 7 ++++ tests/tools/pom.xml | 9 ++++- 7 files changed, 68 insertions(+), 18 deletions(-) diff --git a/client/src/main/java/org/glassfish/tyrus/client/ClientManager.java b/client/src/main/java/org/glassfish/tyrus/client/ClientManager.java index 6469f742..987722da 100755 --- a/client/src/main/java/org/glassfish/tyrus/client/ClientManager.java +++ b/client/src/main/java/org/glassfish/tyrus/client/ClientManager.java @@ -290,31 +290,52 @@ public boolean evaluate() { } @Override - public Session connectToServer(Class annotatedEndpointClass, URI path) throws DeploymentException, IOException { + public Session connectToServer(final Class annotatedEndpointClass, final URI path) throws DeploymentException, IOException { if (annotatedEndpointClass.getAnnotation(ClientEndpoint.class) == null) { throw new DeploymentException( String.format( "Class argument in connectToServer(Class, URI) is to be annotated endpoint class. Class " + "%s does not have @ClientEndpoint", annotatedEndpointClass.getName())); } - return tryCatchInterruptedExecutionEx(() -> connectToServer(annotatedEndpointClass, null, path.toString(), true)); + return tryCatchInterruptedExecutionEx(new SupplierWithEx, DeploymentException>() { + @Override + public Future get() throws DeploymentException { + return connectToServer(annotatedEndpointClass, null, path.toString(), true); + } + }); } @Override - public Session connectToServer(Class endpointClass, ClientEndpointConfig cec, URI path) throws - DeploymentException, IOException { - return tryCatchInterruptedExecutionEx(() -> connectToServer(endpointClass, cec, path.toString(), true)); + public Session connectToServer(final Class endpointClass, + final ClientEndpointConfig cec, + final URI path) throws DeploymentException, IOException { + return tryCatchInterruptedExecutionEx(new SupplierWithEx, DeploymentException>() { + @Override + public Future get() throws DeploymentException { + return connectToServer(endpointClass, cec, path.toString(), true); + } + }); } @Override - public Session connectToServer(Endpoint endpointInstance, ClientEndpointConfig cec, URI path) throws + public Session connectToServer(final Endpoint endpointInstance, final ClientEndpointConfig cec, final URI path) throws DeploymentException, IOException { - return tryCatchInterruptedExecutionEx(() -> connectToServer(endpointInstance, cec, path.toString(), true)); + return tryCatchInterruptedExecutionEx(new SupplierWithEx, DeploymentException>() { + @Override + public Future get() throws DeploymentException { + return connectToServer(endpointInstance, cec, path.toString(), true); + } + }); } @Override - public Session connectToServer(Object obj, URI path) throws DeploymentException, IOException { - return tryCatchInterruptedExecutionEx(() -> connectToServer(obj, null, path.toString(), true)); + public Session connectToServer(final Object obj, final URI path) throws DeploymentException, IOException { + return tryCatchInterruptedExecutionEx(new SupplierWithEx, DeploymentException>() { + @Override + public Future get() throws DeploymentException { + return connectToServer(obj, null, path.toString(), true); + } + }); } private Session tryCatchInterruptedExecutionEx(SupplierWithEx, DeploymentException> supplier) diff --git a/core/src/main/java/org/glassfish/tyrus/core/AnnotatedEndpoint.java b/core/src/main/java/org/glassfish/tyrus/core/AnnotatedEndpoint.java index 8ae82b0e..caedc28b 100755 --- a/core/src/main/java/org/glassfish/tyrus/core/AnnotatedEndpoint.java +++ b/core/src/main/java/org/glassfish/tyrus/core/AnnotatedEndpoint.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -94,7 +94,7 @@ public static AnnotatedEndpoint fromClass(Class annotatedClass, ComponentProv boolean isServerEndpoint, int incomingBufferSize, ErrorCollector collector, EndpointEventListener endpointEventListener) { return fromClass(annotatedClass, componentProvider, isServerEndpoint, incomingBufferSize, collector, - endpointEventListener, Collections.emptySet()); + endpointEventListener, (Set) (Set) Collections.emptySet()); } /** @@ -131,7 +131,7 @@ public static AnnotatedEndpoint fromInstance( Object annotatedInstance, ComponentProviderService componentProvider, boolean isServerEndpoint, int incomingBufferSize, ErrorCollector collector) { return fromInstance(annotatedInstance, componentProvider, isServerEndpoint, incomingBufferSize, - collector, Collections.emptySet()); + collector, (Set) (Set) Collections.emptySet()); } /** diff --git a/core/src/main/java/org/glassfish/tyrus/core/TyrusServerEndpointConfigurator.java b/core/src/main/java/org/glassfish/tyrus/core/TyrusServerEndpointConfigurator.java index f89c1daf..7c4094a0 100644 --- a/core/src/main/java/org/glassfish/tyrus/core/TyrusServerEndpointConfigurator.java +++ b/core/src/main/java/org/glassfish/tyrus/core/TyrusServerEndpointConfigurator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -28,6 +28,7 @@ import javax.websocket.server.ServerEndpointConfig; import org.glassfish.tyrus.core.collection.LazyValue; +import org.glassfish.tyrus.core.collection.Value; import org.glassfish.tyrus.core.collection.Values; import org.glassfish.tyrus.core.extension.ExtendedExtension; import org.glassfish.tyrus.core.frame.Frame; @@ -42,7 +43,12 @@ public class TyrusServerEndpointConfigurator extends ServerEndpointConfig.Config private LazyValue componentProviderService; public TyrusServerEndpointConfigurator() { - this.componentProviderService = Values.lazy(() -> ComponentProviderService.create()); + this.componentProviderService = Values.lazy(new Value() { + @Override + public ComponentProviderService get() { + return ComponentProviderService.create(); + } + }); } @Override diff --git a/ext/client-java8/pom.xml b/ext/client-java8/pom.xml index dc4e42cc..72379a4f 100644 --- a/ext/client-java8/pom.xml +++ b/ext/client-java8/pom.xml @@ -1,6 +1,6 @@