This is an automated email from the ASF dual-hosted git repository. markt pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit 4f90bff128c6cb39bd0ebb8cf1eb6565229dcf86 Author: Mark Thomas <ma...@apache.org> AuthorDate: Tue May 4 17:02:38 2021 +0100 First part of fix for BZ 65262. Make WebSocket more IoC friendly This first part tackles the Endpoints. Endpoint instance creation has been refactored and will now run through the InstanceManger for object creation where possible. Where that isn't possible, the objects will continue to be registered with the InstanceManager. https://bz.apache.org/bugzilla/show_bug.cgi?id=65262 --- .../tomcat/websocket/ClientEndpointHolder.java | 27 +++++++ .../tomcat/websocket/EndpointClassHolder.java | 57 ++++++++++++++ .../apache/tomcat/websocket/EndpointHolder.java | 56 ++++++++++++++ .../tomcat/websocket/LocalStrings.properties | 5 +- .../tomcat/websocket/LocalStrings_fr.properties | 1 - .../tomcat/websocket/LocalStrings_ja.properties | 1 - .../tomcat/websocket/LocalStrings_ko.properties | 1 - .../tomcat/websocket/LocalStrings_zh_CN.properties | 1 - .../apache/tomcat/websocket/PojoClassHolder.java | 63 +++++++++++++++ java/org/apache/tomcat/websocket/PojoHolder.java | 60 +++++++++++++++ java/org/apache/tomcat/websocket/WsSession.java | 84 +++++++++++++------- .../tomcat/websocket/WsWebSocketContainer.java | 90 +++++++++------------- .../tomcat/websocket/pojo/LocalStrings.properties | 2 - .../websocket/pojo/LocalStrings_cs.properties | 2 - .../websocket/pojo/LocalStrings_de.properties | 2 - .../websocket/pojo/LocalStrings_es.properties | 2 - .../websocket/pojo/LocalStrings_fr.properties | 2 - .../websocket/pojo/LocalStrings_ja.properties | 2 - .../websocket/pojo/LocalStrings_ko.properties | 2 - .../websocket/pojo/LocalStrings_zh_CN.properties | 2 - .../tomcat/websocket/pojo/PojoEndpointServer.java | 19 +---- webapps/docs/changelog.xml | 5 ++ 22 files changed, 368 insertions(+), 118 deletions(-) diff --git a/java/org/apache/tomcat/websocket/ClientEndpointHolder.java b/java/org/apache/tomcat/websocket/ClientEndpointHolder.java new file mode 100644 index 0000000..d3334d1 --- /dev/null +++ b/java/org/apache/tomcat/websocket/ClientEndpointHolder.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.websocket; + +import jakarta.websocket.DeploymentException; +import jakarta.websocket.Endpoint; + +import org.apache.tomcat.InstanceManager; + +public interface ClientEndpointHolder { + String getClassName(); + Endpoint getInstance(InstanceManager instanceManager) throws DeploymentException; +} diff --git a/java/org/apache/tomcat/websocket/EndpointClassHolder.java b/java/org/apache/tomcat/websocket/EndpointClassHolder.java new file mode 100644 index 0000000..7a2cf21 --- /dev/null +++ b/java/org/apache/tomcat/websocket/EndpointClassHolder.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.websocket; + +import javax.naming.NamingException; + +import jakarta.websocket.DeploymentException; +import jakarta.websocket.Endpoint; + +import org.apache.tomcat.InstanceManager; +import org.apache.tomcat.util.res.StringManager; + +public class EndpointClassHolder implements ClientEndpointHolder { + + private static final StringManager sm = StringManager.getManager(EndpointClassHolder.class); + + private final Class<? extends Endpoint> clazz; + + + public EndpointClassHolder(Class<? extends Endpoint> clazz) { + this.clazz = clazz; + } + + + @Override + public String getClassName() { + return clazz.getName(); + } + + + @Override + public Endpoint getInstance(InstanceManager instanceManager) throws DeploymentException { + try { + if (instanceManager == null) { + return clazz.getConstructor().newInstance(); + } else { + return (Endpoint) instanceManager.newInstance(clazz); + } + } catch (ReflectiveOperationException | NamingException e) { + throw new DeploymentException(sm.getString("clientEndpointHolder.instanceCreationFailed"), e); + } + } +} diff --git a/java/org/apache/tomcat/websocket/EndpointHolder.java b/java/org/apache/tomcat/websocket/EndpointHolder.java new file mode 100644 index 0000000..dcf9a9a --- /dev/null +++ b/java/org/apache/tomcat/websocket/EndpointHolder.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.websocket; + +import javax.naming.NamingException; + +import jakarta.websocket.DeploymentException; +import jakarta.websocket.Endpoint; + +import org.apache.tomcat.InstanceManager; +import org.apache.tomcat.util.res.StringManager; + +public class EndpointHolder implements ClientEndpointHolder { + + private static final StringManager sm = StringManager.getManager(EndpointHolder.class); + + private final Endpoint endpoint; + + + public EndpointHolder(Endpoint endpoint) { + this.endpoint = endpoint; + } + + + @Override + public String getClassName() { + return endpoint.getClass().getName(); + } + + + @Override + public Endpoint getInstance(InstanceManager instanceManager) throws DeploymentException { + if (instanceManager != null) { + try { + instanceManager.newInstance(endpoint); + } catch (ReflectiveOperationException | NamingException e) { + throw new DeploymentException(sm.getString("clientEndpointHolder.instanceRegistrationFailed"), e); + } + } + return endpoint; + } +} diff --git a/java/org/apache/tomcat/websocket/LocalStrings.properties b/java/org/apache/tomcat/websocket/LocalStrings.properties index 67630ba..3ee86ae 100644 --- a/java/org/apache/tomcat/websocket/LocalStrings.properties +++ b/java/org/apache/tomcat/websocket/LocalStrings.properties @@ -33,6 +33,9 @@ backgroundProcessManager.processFailed=A background process failed caseInsensitiveKeyMap.nullKey=Null keys are not permitted +clientEndpointHolder.instanceCreationFailed=Failed to create WebSocketEndpoint +clientEndpointHolder.instanceRegistrationFailed=Failed to register Endpoint instance with the InstanceManager + futureToSendHandler.timeout=Operation timed out after waiting [{0}] [{1}] to complete perMessageDeflate.alreadyClosed=The transformer has been closed and may no longer be used @@ -114,6 +117,7 @@ wsSession.duplicateHandlerBinary=A binary message handler has already been confi wsSession.duplicateHandlerPong=A pong message handler has already been configured wsSession.duplicateHandlerText=A text message handler has already been configured wsSession.flushFailOnClose=Failed to flush batched messages on session close +wsSession.instanceCreateFailed=Endpoint instance creation failed wsSession.instanceNew=Endpoint instance registration failed wsSession.invalidHandlerTypePong=A pong message handler must implement MessageHandler.Whole wsSession.messageFailed=Unable to write the complete message as the WebSocket connection has been closed @@ -132,7 +136,6 @@ wsWebSocketContainer.asynchronousSocketChannelFail=Unable to open a connection t wsWebSocketContainer.connect.entry=Connecting endpoint instance of type [{0}] to [{1}] wsWebSocketContainer.connect.write=Writing the HTTP upgrade request from buffer starting at [{0}] with a limit of [{1}] from local address [{2}] wsWebSocketContainer.defaultConfiguratorFail=Failed to create the default configurator -wsWebSocketContainer.endpointCreateFail=Failed to create a local endpoint of type [{0}] wsWebSocketContainer.failedAuthentication=Failed to handle HTTP response code [{0}]. Authentication header was not accepted by server. wsWebSocketContainer.httpRequestFailed=The HTTP request to initiate the WebSocket connection to [{0}] failed wsWebSocketContainer.invalidExtensionParameters=The server responded with extension parameters the client is unable to support diff --git a/java/org/apache/tomcat/websocket/LocalStrings_fr.properties b/java/org/apache/tomcat/websocket/LocalStrings_fr.properties index 0a8d5e8..4d12b3c 100644 --- a/java/org/apache/tomcat/websocket/LocalStrings_fr.properties +++ b/java/org/apache/tomcat/websocket/LocalStrings_fr.properties @@ -117,7 +117,6 @@ wsWebSocketContainer.asynchronousSocketChannelFail=Impossible d'ouvrir une conne wsWebSocketContainer.connect.entry=Connection à l''instance d''endpoint de type [{0}] à [{1}] wsWebSocketContainer.connect.write=Ecriture de la requête d''upgrade HTTP depuis le tampon à partir de [{0}] avec une limite de [{1}] depuis l''adresse locale [{2}] wsWebSocketContainer.defaultConfiguratorFail=Impossible de créer le configurateur par défaut -wsWebSocketContainer.endpointCreateFail=Echec de création d''un point d''entrée local de type [{0}] wsWebSocketContainer.failedAuthentication=Echec du traitement du code de réponse HTTP [{0}], l''en-tête d''authentification n''a pas été accepté par le serveur wsWebSocketContainer.httpRequestFailed=La requête HTTP pour initier la connection WebSocket a échoué wsWebSocketContainer.invalidExtensionParameters=Le serveur a répondu avec des paramètres d'extension que le client n'est pas capable de traiter diff --git a/java/org/apache/tomcat/websocket/LocalStrings_ja.properties b/java/org/apache/tomcat/websocket/LocalStrings_ja.properties index 912995a..daf9dc8 100644 --- a/java/org/apache/tomcat/websocket/LocalStrings_ja.properties +++ b/java/org/apache/tomcat/websocket/LocalStrings_ja.properties @@ -117,7 +117,6 @@ wsWebSocketContainer.asynchronousSocketChannelFail=サーバーへの接続を wsWebSocketContainer.connect.entry=タイプ[{0}]のエンドポイントインスタンスを[{1}]に接続しています wsWebSocketContainer.connect.write=ローカルアドレス[{2}]から[{1}]の制限で[{0}]から始まるバッファからHTTPアップグレードリクエストを書き込んでいます wsWebSocketContainer.defaultConfiguratorFail=既定のコンフィグレータ生成に失敗しました。 -wsWebSocketContainer.endpointCreateFail=クラス [{0}] のローカルエンドポイントを作成できません。 wsWebSocketContainer.failedAuthentication=HTTP応答コード[{0}]を処理できませんでした。 認証ヘッダーがサーバーによって受け入れられませんでした。 wsWebSocketContainer.httpRequestFailed=WebSocket接続を開始するHTTPリクエストが失敗しました。 wsWebSocketContainer.invalidExtensionParameters=サーバーはクライアントの解釈できない拡張パラメーターで応答しました。 diff --git a/java/org/apache/tomcat/websocket/LocalStrings_ko.properties b/java/org/apache/tomcat/websocket/LocalStrings_ko.properties index 1bf4f1f..096ddd2 100644 --- a/java/org/apache/tomcat/websocket/LocalStrings_ko.properties +++ b/java/org/apache/tomcat/websocket/LocalStrings_ko.properties @@ -117,7 +117,6 @@ wsWebSocketContainer.asynchronousSocketChannelFail=서버에 대한 연결을 wsWebSocketContainer.connect.entry=타입이 [{0}]인 엔드포인트 인스턴스를 [{1}]에 연결합니다. wsWebSocketContainer.connect.write=로컬 주소 [{2}](으)로부터, 시작 위치 [{0}], 최대 길이 [{1}]의 데이터를 버퍼에 씁니다. wsWebSocketContainer.defaultConfiguratorFail=기본 Configurator를 생성하지 못했습니다. -wsWebSocketContainer.endpointCreateFail=타입이 [{0}]인 로컬 엔드포인트를 생성하지 못했습니다. wsWebSocketContainer.failedAuthentication=HTTP 응답 코드 [{0}]을(를) 처리하지 못했습니다. 인증 헤더가 서버에 의해 받아들여지지 않았습니다. wsWebSocketContainer.httpRequestFailed=웹소켓 연결을 초기화하기 위한 HTTP 요청이 실패했습니다. wsWebSocketContainer.invalidExtensionParameters=서버가, 클라이언트가 지원할 수 없는 확장 파라미터들과 함께 응답했습니다. diff --git a/java/org/apache/tomcat/websocket/LocalStrings_zh_CN.properties b/java/org/apache/tomcat/websocket/LocalStrings_zh_CN.properties index 71b16d6..9a06489 100644 --- a/java/org/apache/tomcat/websocket/LocalStrings_zh_CN.properties +++ b/java/org/apache/tomcat/websocket/LocalStrings_zh_CN.properties @@ -115,7 +115,6 @@ wsSession.unknownHandlerType=无法添加消息处理程序[{0}],因为它被 wsWebSocketContainer.asynchronousSocketChannelFail=无法打开与服务器的连接 wsWebSocketContainer.connect.entry=连接[{0}]类型的终端实例至[{1}] wsWebSocketContainer.defaultConfiguratorFail=无法创建默认配置程序。 -wsWebSocketContainer.endpointCreateFail=未能创建类型为[{0}]的本地终结点 wsWebSocketContainer.failedAuthentication=无法处理http响应代码[{0}]。服务器不接受身份验证头。 wsWebSocketContainer.httpRequestFailed=启动WebSocket连接的HTTP请求失败 wsWebSocketContainer.invalidExtensionParameters=服务器用客户端无法支持的扩展参数响应 diff --git a/java/org/apache/tomcat/websocket/PojoClassHolder.java b/java/org/apache/tomcat/websocket/PojoClassHolder.java new file mode 100644 index 0000000..e2c1da8 --- /dev/null +++ b/java/org/apache/tomcat/websocket/PojoClassHolder.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.websocket; + +import javax.naming.NamingException; + +import jakarta.websocket.ClientEndpointConfig; +import jakarta.websocket.DeploymentException; +import jakarta.websocket.Endpoint; + +import org.apache.tomcat.InstanceManager; +import org.apache.tomcat.util.res.StringManager; +import org.apache.tomcat.websocket.pojo.PojoEndpointClient; + +public class PojoClassHolder implements ClientEndpointHolder { + + private static final StringManager sm = StringManager.getManager(PojoClassHolder.class); + + private final Class<?> pojoClazz; + private final ClientEndpointConfig clientEndpointConfig; + + + public PojoClassHolder(Class<?> pojoClazz, ClientEndpointConfig clientEndpointConfig) { + this.pojoClazz = pojoClazz; + this.clientEndpointConfig = clientEndpointConfig; + } + + + @Override + public String getClassName() { + return pojoClazz.getName(); + } + + @Override + public Endpoint getInstance(InstanceManager instanceManager) throws DeploymentException { + try { + Object pojo; + if (instanceManager == null) { + pojo = pojoClazz.getConstructor().newInstance(); + } else { + pojo = instanceManager.newInstance(pojoClazz); + } + return new PojoEndpointClient(pojo, clientEndpointConfig.getDecoders()); + } catch (ReflectiveOperationException | SecurityException | NamingException e) { + throw new DeploymentException(sm.getString("clientEndpointHolder.instanceCreationFailed"), e); + } + } + +} diff --git a/java/org/apache/tomcat/websocket/PojoHolder.java b/java/org/apache/tomcat/websocket/PojoHolder.java new file mode 100644 index 0000000..7195944 --- /dev/null +++ b/java/org/apache/tomcat/websocket/PojoHolder.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.websocket; + +import javax.naming.NamingException; + +import jakarta.websocket.ClientEndpointConfig; +import jakarta.websocket.DeploymentException; +import jakarta.websocket.Endpoint; + +import org.apache.tomcat.InstanceManager; +import org.apache.tomcat.util.res.StringManager; +import org.apache.tomcat.websocket.pojo.PojoEndpointClient; + +public class PojoHolder implements ClientEndpointHolder { + + private static final StringManager sm = StringManager.getManager(PojoHolder.class); + + private final Object pojo; + private final ClientEndpointConfig clientEndpointConfig; + + + public PojoHolder(Object pojo, ClientEndpointConfig clientEndpointConfig) { + this.pojo = pojo; + this.clientEndpointConfig = clientEndpointConfig; + } + + + @Override + public String getClassName() { + return pojo.getClass().getName(); + } + + + @Override + public Endpoint getInstance(InstanceManager instanceManager) throws DeploymentException { + if (instanceManager != null) { + try { + instanceManager.newInstance(pojo); + } catch (ReflectiveOperationException | NamingException e) { + throw new DeploymentException(sm.getString("clientEndpointHolder.instanceRegistrationFailed"), e); + } + } + return new PojoEndpointClient(pojo, clientEndpointConfig.getDecoders()); + } +} diff --git a/java/org/apache/tomcat/websocket/WsSession.java b/java/org/apache/tomcat/websocket/WsSession.java index 2085cb3..4c71d84 100644 --- a/java/org/apache/tomcat/websocket/WsSession.java +++ b/java/org/apache/tomcat/websocket/WsSession.java @@ -30,6 +30,8 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; +import javax.naming.NamingException; + import jakarta.websocket.ClientEndpointConfig; import jakarta.websocket.CloseReason; import jakarta.websocket.CloseReason.CloseCode; @@ -47,6 +49,7 @@ import jakarta.websocket.SendResult; import jakarta.websocket.Session; import jakarta.websocket.WebSocketContainer; import jakarta.websocket.server.ServerEndpointConfig; +import jakarta.websocket.server.ServerEndpointConfig.Configurator; import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; @@ -55,19 +58,29 @@ import org.apache.tomcat.InstanceManagerBindings; import org.apache.tomcat.util.ExceptionUtils; import org.apache.tomcat.util.res.StringManager; import org.apache.tomcat.websocket.pojo.PojoEndpointServer; +import org.apache.tomcat.websocket.server.DefaultServerEndpointConfigurator; public class WsSession implements Session { + private final Log log = LogFactory.getLog(WsSession.class); // must not be static + private static final StringManager sm = StringManager.getManager(WsSession.class); + // An ellipsis is a single character that looks like three periods in a row // and is used to indicate a continuation. private static final byte[] ELLIPSIS_BYTES = "\u2026".getBytes(StandardCharsets.UTF_8); // An ellipsis is three bytes in UTF-8 private static final int ELLIPSIS_BYTES_LEN = ELLIPSIS_BYTES.length; - private static final StringManager sm = StringManager.getManager(WsSession.class); + private static final boolean SEC_CONFIGURATOR_USES_IMPL_DEFAULT; + private static AtomicLong ids = new AtomicLong(0); - private final Log log = LogFactory.getLog(WsSession.class); // must not be static + static { + ServerEndpointConfig.Builder builder = ServerEndpointConfig.Builder.create(null, null); + ServerEndpointConfig sec = builder.build(); + SEC_CONFIGURATOR_USES_IMPL_DEFAULT = + sec.getConfigurator().getClass().equals(DefaultServerEndpointConfigurator.class); + } private final Endpoint localEndpoint; private final WsRemoteEndpointImplBase wsRemoteEndpoint; @@ -111,7 +124,7 @@ public class WsSession implements Session { * called will be used when calling * {@link Endpoint#onClose(Session, CloseReason)}. * - * @param localEndpoint The end point managed by this code + * @param clientEndpointHolder The end point managed by this code * @param wsRemoteEndpoint The other / remote end point * @param wsWebSocketContainer The container that created this session * @param negotiatedExtensions The agreed extensions to use for this session @@ -126,12 +139,11 @@ public class WsSession implements Session { * end point * @throws DeploymentException if an invalid encode is specified */ - public WsSession(Endpoint localEndpoint, + public WsSession(ClientEndpointHolder clientEndpointHolder, WsRemoteEndpointImplBase wsRemoteEndpoint, WsWebSocketContainer wsWebSocketContainer, List<Extension> negotiatedExtensions, String subProtocol, Map<String, String> pathParameters, boolean secure, ClientEndpointConfig clientEndpointConfig) throws DeploymentException { - this.localEndpoint = localEndpoint; this.wsRemoteEndpoint = wsRemoteEndpoint; this.wsRemoteEndpoint.setSession(this); this.remoteEndpointAsync = new WsRemoteEndpointAsync(wsRemoteEndpoint); @@ -165,13 +177,8 @@ public class WsSession implements Session { if (instanceManager == null) { instanceManager = InstanceManagerBindings.get(applicationClassLoader); } - if (instanceManager != null) { - try { - instanceManager.newInstance(localEndpoint); - } catch (Exception e) { - throw new DeploymentException(sm.getString("wsSession.instanceNew"), e); - } - } + + this.localEndpoint = clientEndpointHolder.getInstance(instanceManager); if (log.isDebugEnabled()) { log.debug(sm.getString("wsSession.created", id)); @@ -221,17 +228,6 @@ public class WsSession implements Session { List<Extension> negotiatedExtensions, String subProtocol, Map<String, String> pathParameters, boolean secure, ServerEndpointConfig serverEndpointConfig) throws DeploymentException { - try { - Class<?> clazz = serverEndpointConfig.getEndpointClass(); - if (Endpoint.class.isAssignableFrom(clazz)) { - this.localEndpoint = (Endpoint) serverEndpointConfig.getConfigurator().getEndpointInstance(clazz); - } else { - this.localEndpoint = new PojoEndpointServer(pathParameters); - } - } catch (InstantiationException e) { - throw new DeploymentException(sm.getString("wsSession.instanceNew"), e); - } - this.wsRemoteEndpoint = wsRemoteEndpoint; this.wsRemoteEndpoint.setSession(this); this.remoteEndpointAsync = new WsRemoteEndpointAsync(wsRemoteEndpoint); @@ -269,12 +265,32 @@ public class WsSession implements Session { if (instanceManager == null) { instanceManager = InstanceManagerBindings.get(applicationClassLoader); } - if (instanceManager != null) { - try { - instanceManager.newInstance(localEndpoint); - } catch (Exception e) { - throw new DeploymentException(sm.getString("wsSession.instanceNew"), e); + + Configurator configurator = serverEndpointConfig.getConfigurator(); + Class<?> clazz = serverEndpointConfig.getEndpointClass(); + + Object endpointInstance; + try { + if (instanceManager == null || !isDefaultConfigurator(configurator)) { + endpointInstance = configurator.getEndpointInstance(clazz); + if (instanceManager != null) { + try { + instanceManager.newInstance(endpointInstance); + } catch (ReflectiveOperationException | NamingException e) { + throw new DeploymentException(sm.getString("wsSession.instanceNew"), e); + } + } + } else { + endpointInstance = instanceManager.newInstance(clazz); } + } catch (ReflectiveOperationException | NamingException e) { + throw new DeploymentException(sm.getString("wsSession.instanceCreateFailed"), e); + } + + if (endpointInstance instanceof Endpoint) { + this.localEndpoint = (Endpoint) endpointInstance; + } else { + this.localEndpoint = new PojoEndpointServer(pathParameters, endpointInstance); } if (log.isDebugEnabled()) { @@ -283,6 +299,18 @@ public class WsSession implements Session { } + private boolean isDefaultConfigurator(Configurator configurator) { + if (configurator.getClass().equals(DefaultServerEndpointConfigurator.class)) { + return true; + } + if (SEC_CONFIGURATOR_USES_IMPL_DEFAULT && + configurator.getClass().equals(ServerEndpointConfig.Configurator.class)) { + return true; + } + return false; + } + + /** * Creates a new WebSocket session for communication between the two * provided end points. The result of {@link Thread#getContextClassLoader()} diff --git a/java/org/apache/tomcat/websocket/WsWebSocketContainer.java b/java/org/apache/tomcat/websocket/WsWebSocketContainer.java index 71d2f70..aee1994 100644 --- a/java/org/apache/tomcat/websocket/WsWebSocketContainer.java +++ b/java/org/apache/tomcat/websocket/WsWebSocketContainer.java @@ -74,7 +74,6 @@ import org.apache.tomcat.util.codec.binary.Base64; import org.apache.tomcat.util.collections.CaseInsensitiveKeyMap; import org.apache.tomcat.util.res.StringManager; import org.apache.tomcat.util.security.KeyStoreUtil; -import org.apache.tomcat.websocket.pojo.PojoEndpointClient; public class WsWebSocketContainer implements WebSocketContainer, BackgroundProcess { @@ -115,19 +114,30 @@ public class WsWebSocketContainer implements WebSocketContainer, BackgroundProce } @Override - public Session connectToServer(Object pojo, URI path) - throws DeploymentException { + public Session connectToServer(Object pojo, URI path) throws DeploymentException { + ClientEndpointConfig config = createClientEndpointConfig(pojo.getClass()); + ClientEndpointHolder holder = new PojoHolder(pojo, config); + return connectToServerRecursive(holder, config, path, new HashSet<>()); + } + + + @Override + public Session connectToServer(Class<?> annotatedEndpointClass, URI path) throws DeploymentException { + ClientEndpointConfig config = createClientEndpointConfig(annotatedEndpointClass); + ClientEndpointHolder holder = new PojoClassHolder(annotatedEndpointClass, config); + return connectToServerRecursive(holder, config, path, new HashSet<>()); + } + - ClientEndpoint annotation = - pojo.getClass().getAnnotation(ClientEndpoint.class); + private ClientEndpointConfig createClientEndpointConfig(Class<?> annotatedEndpointClass) + throws DeploymentException { + ClientEndpoint annotation = annotatedEndpointClass.getAnnotation(ClientEndpoint.class); if (annotation == null) { throw new DeploymentException( sm.getString("wsWebSocketContainer.missingAnnotation", - pojo.getClass().getName())); + annotatedEndpointClass.getName())); } - Endpoint ep = new PojoEndpointClient(pojo, Arrays.asList(annotation.decoders())); - Class<? extends ClientEndpointConfig.Configurator> configuratorClazz = annotation.configurator(); @@ -152,59 +162,33 @@ public class WsWebSocketContainer implements WebSocketContainer, BackgroundProce encoders(Arrays.asList(annotation.encoders())). preferredSubprotocols(Arrays.asList(annotation.subprotocols())). build(); - return connectToServer(ep, config, path); - } - - @Override - public Session connectToServer(Class<?> annotatedEndpointClass, URI path) - throws DeploymentException { - - Object pojo; - try { - pojo = annotatedEndpointClass.getConstructor().newInstance(); - } catch (ReflectiveOperationException e) { - throw new DeploymentException(sm.getString( - "wsWebSocketContainer.endpointCreateFail", - annotatedEndpointClass.getName()), e); - } - - return connectToServer(pojo, path); + return config; } @Override - public Session connectToServer(Class<? extends Endpoint> clazz, - ClientEndpointConfig clientEndpointConfiguration, URI path) - throws DeploymentException { - - Endpoint endpoint; - try { - endpoint = clazz.getConstructor().newInstance(); - } catch (ReflectiveOperationException e) { - throw new DeploymentException(sm.getString( - "wsWebSocketContainer.endpointCreateFail", clazz.getName()), - e); - } - - return connectToServer(endpoint, clientEndpointConfiguration, path); + public Session connectToServer(Class<? extends Endpoint> clazz, ClientEndpointConfig clientEndpointConfiguration, + URI path) throws DeploymentException { + ClientEndpointHolder holder = new EndpointClassHolder(clazz); + return connectToServerRecursive(holder, clientEndpointConfiguration, path, new HashSet<>()); } @Override - public Session connectToServer(Endpoint endpoint, - ClientEndpointConfig clientEndpointConfiguration, URI path) + public Session connectToServer(Endpoint endpoint, ClientEndpointConfig clientEndpointConfiguration, URI path) throws DeploymentException { - return connectToServerRecursive(endpoint, clientEndpointConfiguration, path, new HashSet<>()); + ClientEndpointHolder holder = new EndpointHolder(endpoint); + return connectToServerRecursive(holder, clientEndpointConfiguration, path, new HashSet<>()); } - private Session connectToServerRecursive(Endpoint endpoint, - ClientEndpointConfig clientEndpointConfiguration, URI path, - Set<URI> redirectSet) + + private Session connectToServerRecursive(ClientEndpointHolder clientEndpointHolder, + ClientEndpointConfig clientEndpointConfiguration, URI path, Set<URI> redirectSet) throws DeploymentException { if (log.isDebugEnabled()) { - log.debug(sm.getString("wsWebSocketContainer.connect.entry", endpoint.getClass().getName(), path)); + log.debug(sm.getString("wsWebSocketContainer.connect.entry", clientEndpointHolder.getClassName(), path)); } boolean secure = false; @@ -398,7 +382,8 @@ public class WsWebSocketContainer implements WebSocketContainer, BackgroundProce Integer.toString(maxRedirects))); } - return connectToServerRecursive(endpoint, clientEndpointConfiguration, redirectLocation, redirectSet); + return connectToServerRecursive( + clientEndpointHolder, clientEndpointConfiguration, redirectLocation, redirectSet); } @@ -435,7 +420,8 @@ public class WsWebSocketContainer implements WebSocketContainer, BackgroundProce userProperties.put(Constants.AUTHORIZATION_HEADER_NAME, auth.getAuthorization( requestUri, wwwAuthenticateHeaders.get(0), userProperties)); - return connectToServerRecursive(endpoint, clientEndpointConfiguration, path, redirectSet); + return connectToServerRecursive( + clientEndpointHolder, clientEndpointConfiguration, path, redirectSet); } else { throw new DeploymentException(sm.getString("wsWebSocketContainer.invalidStatus", @@ -506,8 +492,8 @@ public class WsWebSocketContainer implements WebSocketContainer, BackgroundProce // Switch to WebSocket WsRemoteEndpointImplClient wsRemoteEndpointClient = new WsRemoteEndpointImplClient(channel); - WsSession wsSession = new WsSession(endpoint, wsRemoteEndpointClient, this, extensionsAgreed, subProtocol, - Collections.<String,String>emptyMap(), secure, clientEndpointConfiguration); + WsSession wsSession = new WsSession(clientEndpointHolder, wsRemoteEndpointClient, this, extensionsAgreed, + subProtocol, Collections.<String,String>emptyMap(), secure, clientEndpointConfiguration); WsFrameClient wsFrameClient = new WsFrameClient(response, channel, wsSession, transformation); @@ -515,8 +501,8 @@ public class WsWebSocketContainer implements WebSocketContainer, BackgroundProce // completed transformation chain to the remote end point. wsRemoteEndpointClient.setTransformation(wsFrameClient.getTransformation()); - endpoint.onOpen(wsSession, clientEndpointConfiguration); - registerSession(endpoint, wsSession); + wsSession.getLocal().onOpen(wsSession, clientEndpointConfiguration); + registerSession(wsSession.getLocal(), wsSession); /* It is possible that the server sent one or more messages as soon as * the WebSocket connection was established. Depending on the exact diff --git a/java/org/apache/tomcat/websocket/pojo/LocalStrings.properties b/java/org/apache/tomcat/websocket/pojo/LocalStrings.properties index bca550d..50b47c3 100644 --- a/java/org/apache/tomcat/websocket/pojo/LocalStrings.properties +++ b/java/org/apache/tomcat/websocket/pojo/LocalStrings.properties @@ -19,8 +19,6 @@ pojoEndpointBase.onError=No error handling configured for [{0}] and the followin pojoEndpointBase.onErrorFail=Failed to call onError method of POJO end point for POJO of type [{0}] pojoEndpointBase.onOpenFail=Failed to call onOpen method of POJO end point for POJO of type [{0}] -pojoEndpointServer.getPojoInstanceFail=Failed to create instance of POJO of type [{0}] - pojoMessageHandlerWhole.decodeIoFail=IO error while decoding message pojoMessageHandlerWhole.maxBufferSize=The maximum supported message size for this implementation is Integer.MAX_VALUE diff --git a/java/org/apache/tomcat/websocket/pojo/LocalStrings_cs.properties b/java/org/apache/tomcat/websocket/pojo/LocalStrings_cs.properties index b2c8a29..f0bd452 100644 --- a/java/org/apache/tomcat/websocket/pojo/LocalStrings_cs.properties +++ b/java/org/apache/tomcat/websocket/pojo/LocalStrings_cs.properties @@ -13,8 +13,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -pojoEndpointServer.getPojoInstanceFail=Vytvoření POJO instance typu [{0}] selhalo - pojoMethodMapping.duplicateAnnotation=Duplicitní anotace [{0}] přítomná na třídě [{1}] pojoMethodMapping.duplicatePongMessageParam=Více PongMessage parametrů přitomnýcn na metodě [{0}] třídy [{1}], která byla označena anotací OnMessage pojoMethodMapping.invalidDecoder=Nelze vytvořit instanci specifikovaného dekodéru typu [{0}] diff --git a/java/org/apache/tomcat/websocket/pojo/LocalStrings_de.properties b/java/org/apache/tomcat/websocket/pojo/LocalStrings_de.properties index 26a684a..2bdc86e 100644 --- a/java/org/apache/tomcat/websocket/pojo/LocalStrings_de.properties +++ b/java/org/apache/tomcat/websocket/pojo/LocalStrings_de.properties @@ -13,8 +13,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -pojoEndpointServer.getPojoInstanceFail=Es konnte keine POJO Instanz des Typs [{0}] erzeugt werden - pojoMethodMapping.duplicateAnnotation=Doppelte Annotation [{0}] an Klasse [{1}] gefunden pojoMethodMapping.duplicateLastParam=Es sind mehrere Boolean (letzte) Parameter für die Methode [{0}] der Klasse [{1}], die mit OnMessage annotiert ist, vorhanden. pojoMethodMapping.invalidDecoder=Der angegeben Dekoder vom Typ [{0}] konnte nicht instanziiert werden diff --git a/java/org/apache/tomcat/websocket/pojo/LocalStrings_es.properties b/java/org/apache/tomcat/websocket/pojo/LocalStrings_es.properties index bbef800..0c385fb 100644 --- a/java/org/apache/tomcat/websocket/pojo/LocalStrings_es.properties +++ b/java/org/apache/tomcat/websocket/pojo/LocalStrings_es.properties @@ -16,8 +16,6 @@ pojoEndpointBase.onCloseFail=Fallo al llamar el método onClose del endpoint POJO para el tipo POJO [{0}] pojoEndpointBase.onOpenFail=Fallo al llamar el método onOpen del end point POJO para el tipo POJO [{0}]\n -pojoEndpointServer.getPojoInstanceFail=Fallo al crear la instancia POJO de tipo [{0}]\n - pojoMethodMapping.duplicateAnnotation=Anotaciones duplicadas [{0}] presente en la clase [{1}]\n pojoMethodMapping.duplicatePongMessageParam=Varios parámetros de PongMessage estan presentes en el método [{0}] de la clase [{1}] que fue anotado con OnMessage pojoMethodMapping.invalidDecoder=El decodificador especificado de tipo [{0}] no puede ser instanciado\n diff --git a/java/org/apache/tomcat/websocket/pojo/LocalStrings_fr.properties b/java/org/apache/tomcat/websocket/pojo/LocalStrings_fr.properties index 186ee96..806ef3f 100644 --- a/java/org/apache/tomcat/websocket/pojo/LocalStrings_fr.properties +++ b/java/org/apache/tomcat/websocket/pojo/LocalStrings_fr.properties @@ -19,8 +19,6 @@ pojoEndpointBase.onError=Aucun gestionnaire d''erreur n''est configuré pour [{0 pojoEndpointBase.onErrorFail=Echec de l''appel de la méthode onError du point de terminaison POJO pour le type [{0}] pojoEndpointBase.onOpenFail=Impossible d’appeler la méthode onOpen du point de terminaison POJO de type [{0}] -pojoEndpointServer.getPojoInstanceFail=Échec de création d''une instance de POJO de type [{0}] - pojoMessageHandlerWhole.decodeIoFail=Erreur d'IO lors du décodage du message pojoMessageHandlerWhole.maxBufferSize=La taille maximale de message supportée par cette implémentation est Integer.MAX_VALUE diff --git a/java/org/apache/tomcat/websocket/pojo/LocalStrings_ja.properties b/java/org/apache/tomcat/websocket/pojo/LocalStrings_ja.properties index e28e5e3..35da058 100644 --- a/java/org/apache/tomcat/websocket/pojo/LocalStrings_ja.properties +++ b/java/org/apache/tomcat/websocket/pojo/LocalStrings_ja.properties @@ -19,8 +19,6 @@ pojoEndpointBase.onError=[{0}]に対してエラー処理が構成されてお pojoEndpointBase.onErrorFail=タイプ[{0}]のPOJOのPOJOエンドポイントのonErrorメソッドの呼び出しに失敗しました pojoEndpointBase.onOpenFail=タイプ[{0}]のPOJOのPOJOエンドポイントのonOpenメソッドの呼び出しに失敗しました。 -pojoEndpointServer.getPojoInstanceFail=POJO クラス [{0}] をインスタンス化できませんでした。 - pojoMessageHandlerWhole.decodeIoFail=メッセージの復号中に入出力エラーが発生しました。 pojoMessageHandlerWhole.maxBufferSize=この実装で対応可能なメッセージサイズの上限値は Integer.MAX_VALUE です。 diff --git a/java/org/apache/tomcat/websocket/pojo/LocalStrings_ko.properties b/java/org/apache/tomcat/websocket/pojo/LocalStrings_ko.properties index 78174c8..052eb0d 100644 --- a/java/org/apache/tomcat/websocket/pojo/LocalStrings_ko.properties +++ b/java/org/apache/tomcat/websocket/pojo/LocalStrings_ko.properties @@ -19,8 +19,6 @@ pojoEndpointBase.onError=[{0}]을(를) 위한 오류 핸들링이 설정되지 pojoEndpointBase.onErrorFail=타입이 [{0}]인 POJO를 위한 POJO 엔드포인트의 onError를 호출하지 못했습니다. pojoEndpointBase.onOpenFail=타입이 [{0}]인 POJO를 위한, POJO 엔드포인트의 onOpen 메소드를 호출하지 못했습니다. -pojoEndpointServer.getPojoInstanceFail=타입이 [{0}]인 POJO 인스턴스를 생성하지 못했습니다. - pojoMessageHandlerWhole.decodeIoFail=메시지를 디코딩하는 중 IO 오류 발생 pojoMessageHandlerWhole.maxBufferSize=이 구현을 위해 지원되는 최대 메시지 크기는 Integer.MAX_VALUE입니다. diff --git a/java/org/apache/tomcat/websocket/pojo/LocalStrings_zh_CN.properties b/java/org/apache/tomcat/websocket/pojo/LocalStrings_zh_CN.properties index 3147f4a..ae9909b 100644 --- a/java/org/apache/tomcat/websocket/pojo/LocalStrings_zh_CN.properties +++ b/java/org/apache/tomcat/websocket/pojo/LocalStrings_zh_CN.properties @@ -19,8 +19,6 @@ pojoEndpointBase.onError=在错误发生后,没有为[{0}]配置错误处理 pojoEndpointBase.onErrorFail=无法为类型为[{0}]的POJO调用POJO端点的onError方法 pojoEndpointBase.onOpenFail=无法为类型为[{0}]的POJO调用POJO端点的onOpen方法 -pojoEndpointServer.getPojoInstanceFail=创建类型为 [{0}] 的 POJO 实例失败 - pojoMessageHandlerWhole.decodeIoFail=解码消息时出现IO错误 pojoMessageHandlerWhole.maxBufferSize=此实现支持的最大消息大小为Integer.MAX_VALUE diff --git a/java/org/apache/tomcat/websocket/pojo/PojoEndpointServer.java b/java/org/apache/tomcat/websocket/pojo/PojoEndpointServer.java index 2549d55..e91e10b 100644 --- a/java/org/apache/tomcat/websocket/pojo/PojoEndpointServer.java +++ b/java/org/apache/tomcat/websocket/pojo/PojoEndpointServer.java @@ -22,8 +22,6 @@ import jakarta.websocket.EndpointConfig; import jakarta.websocket.Session; import jakarta.websocket.server.ServerEndpointConfig; -import org.apache.tomcat.util.res.StringManager; - /** * Wrapper class for instances of POJOs annotated with * {@link jakarta.websocket.server.ServerEndpoint} so they appear as standard @@ -31,11 +29,9 @@ import org.apache.tomcat.util.res.StringManager; */ public class PojoEndpointServer extends PojoEndpointBase { - private static final StringManager sm = StringManager.getManager(PojoEndpointServer.class); - - - public PojoEndpointServer(Map<String,String> pathParameters) { + public PojoEndpointServer(Map<String,String> pathParameters, Object pojo) { super(pathParameters); + setPojo(pojo); } @@ -44,17 +40,6 @@ public class PojoEndpointServer extends PojoEndpointBase { ServerEndpointConfig sec = (ServerEndpointConfig) endpointConfig; - Object pojo; - try { - pojo = sec.getConfigurator().getEndpointInstance( - sec.getEndpointClass()); - } catch (InstantiationException e) { - throw new IllegalArgumentException(sm.getString( - "pojoEndpointServer.getPojoInstanceFail", - sec.getEndpointClass().getName()), e); - } - setPojo(pojo); - PojoMethodMapping methodMapping = (PojoMethodMapping) sec.getUserProperties().get( Constants.POJO_METHOD_MAPPING_KEY); diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml index 00ee7d5..391caaf 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -172,6 +172,11 @@ Refactor the way Tomcat passes path parameters to POJO end points to simplify the code. (markt) </scode> + <fix> + <bug>65262</bug>: Refactor the creation of WebSocket end point instances + to be more IoC friendly. Instances are now created via the + <code>InstanceManager</code> where possible. (markt) + </fix> </changelog> </subsection> <subsection name="Web applications"> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org