|
34 | 34 | import jakarta.websocket.HandshakeResponse; |
35 | 35 | import jakarta.websocket.Session; |
36 | 36 | import jakarta.websocket.WebSocketContainer; |
| 37 | +import java.io.Closeable; |
37 | 38 | import java.io.File; |
38 | 39 | import java.io.FileInputStream; |
39 | 40 | import java.io.FileNotFoundException; |
|
84 | 85 | import org.glassfish.tyrus.client.ClientManager; |
85 | 86 | import org.glassfish.tyrus.client.ClientProperties; |
86 | 87 | import org.glassfish.tyrus.client.SslEngineConfigurator; |
| 88 | +import org.glassfish.tyrus.core.BaseContainer; |
87 | 89 | import org.jenkinsci.remoting.engine.Jnlp4ConnectionState; |
88 | 90 | import org.jenkinsci.remoting.engine.JnlpAgentEndpoint; |
89 | 91 | import org.jenkinsci.remoting.engine.JnlpAgentEndpointConfigurator; |
@@ -128,21 +130,7 @@ public class Engine extends Thread { |
128 | 130 | /** |
129 | 131 | * Thread pool that sets {@link #CURRENT}. |
130 | 132 | */ |
131 | | - private final ExecutorService executor = Executors.newCachedThreadPool(new ThreadFactory() { |
132 | | - private final ThreadFactory defaultFactory = Executors.defaultThreadFactory(); |
133 | | - |
134 | | - @Override |
135 | | - public Thread newThread(@NonNull final Runnable r) { |
136 | | - Thread thread = defaultFactory.newThread(() -> { |
137 | | - CURRENT.set(Engine.this); |
138 | | - r.run(); |
139 | | - }); |
140 | | - thread.setDaemon(true); |
141 | | - thread.setUncaughtExceptionHandler( |
142 | | - (t, e) -> LOGGER.log(Level.SEVERE, e, () -> "Uncaught exception in thread " + t)); |
143 | | - return thread; |
144 | | - } |
145 | | - }); |
| 133 | + private final ExecutorService executor; |
146 | 134 |
|
147 | 135 | /** |
148 | 136 | * @deprecated |
@@ -276,6 +264,7 @@ public Engine( |
276 | 264 | String directConnection, |
277 | 265 | String instanceIdentity, |
278 | 266 | Set<String> protocols) { |
| 267 | + executor = makeExecutor(this, agentName); |
279 | 268 | this.listener = listener; |
280 | 269 | this.directConnection = directConnection; |
281 | 270 | if (listener != null) { |
@@ -308,6 +297,25 @@ private static URL ensureTrailingSlash(URL u) { |
308 | 297 | } |
309 | 298 | } |
310 | 299 |
|
| 300 | + private static ExecutorService makeExecutor(Engine engine, String agentName) { |
| 301 | + return Executors.newCachedThreadPool(new ThreadFactory() { |
| 302 | + private final ThreadFactory defaultFactory = |
| 303 | + new NamingThreadFactory(Executors.defaultThreadFactory(), agentName); |
| 304 | + |
| 305 | + @Override |
| 306 | + public Thread newThread(@NonNull final Runnable r) { |
| 307 | + Thread thread = defaultFactory.newThread(() -> { |
| 308 | + CURRENT.set(engine); |
| 309 | + r.run(); |
| 310 | + }); |
| 311 | + thread.setDaemon(true); |
| 312 | + thread.setUncaughtExceptionHandler( |
| 313 | + (t, e) -> LOGGER.log(Level.SEVERE, e, () -> "Uncaught exception in thread " + t)); |
| 314 | + return thread; |
| 315 | + } |
| 316 | + }); |
| 317 | + } |
| 318 | + |
311 | 319 | /** |
312 | 320 | * Starts the engine. |
313 | 321 | * The procedure initializes the working directory and all the required environment |
@@ -538,12 +546,28 @@ public void removeListener(EngineListener el) { |
538 | 546 | } |
539 | 547 |
|
540 | 548 | @Override |
541 | | - @SuppressFBWarnings(value = "HARD_CODE_PASSWORD", justification = "Password doesn't need to be protected.") |
542 | 549 | public void run() { |
543 | | - if (webSocket) { |
544 | | - runWebSocket(); |
545 | | - return; |
| 550 | + try { |
| 551 | + if (webSocket) { |
| 552 | + runWebSocket(); |
| 553 | + } else { |
| 554 | + runTcp(); |
| 555 | + } |
| 556 | + } finally { |
| 557 | + executor.shutdown(); // TODO Java 21 maybe .close() |
| 558 | + if (jarCache instanceof Closeable c) { |
| 559 | + try { |
| 560 | + c.close(); |
| 561 | + } catch (IOException x) { |
| 562 | + LOGGER.log(Level.WARNING, null, x); |
| 563 | + } |
| 564 | + } |
| 565 | + events.completed(); |
546 | 566 | } |
| 567 | + } |
| 568 | + |
| 569 | + @SuppressFBWarnings(value = "HARD_CODE_PASSWORD", justification = "Password doesn't need to be protected.") |
| 570 | + private void runTcp() { |
547 | 571 | // Create the engine |
548 | 572 | try { |
549 | 573 | try (IOHub hub = IOHub.create(executor)) { |
@@ -750,68 +774,73 @@ public void closeRead() throws IOException { |
750 | 774 | SSLContext sslContext = getSSLContext(candidateCertificates, disableHttpsCertValidation); |
751 | 775 | String wsUrl = hudsonUrl.toString().replaceFirst("^http", "ws"); |
752 | 776 | WebSocketContainer container = ContainerProvider.getWebSocketContainer(); |
753 | | - if (container instanceof ClientManager) { |
754 | | - ClientManager client = (ClientManager) container; |
755 | | - |
756 | | - String proxyHost = System.getProperty("http.proxyHost", System.getenv("proxy_host")); |
757 | | - String proxyPort = System.getProperty("http.proxyPort"); |
758 | | - if (proxyHost != null |
759 | | - && "http".equals(hudsonUrl.getProtocol()) |
760 | | - && NoProxyEvaluator.shouldProxy(hudsonUrl.getHost())) { |
761 | | - URI proxyUri; |
762 | | - if (proxyPort != null) { |
763 | | - proxyUri = URI.create(String.format("http://%s:%s", proxyHost, proxyPort)); |
764 | | - } else { |
765 | | - proxyUri = URI.create(String.format("http://%s", proxyHost)); |
766 | | - } |
767 | | - client.getProperties().put(ClientProperties.PROXY_URI, proxyUri); |
768 | | - if (proxyCredentials != null) { |
769 | | - client.getProperties() |
770 | | - .put( |
771 | | - ClientProperties.PROXY_HEADERS, |
772 | | - Map.of( |
773 | | - "Proxy-Authorization", |
774 | | - "Basic " |
775 | | - + Base64.getEncoder() |
776 | | - .encodeToString(proxyCredentials.getBytes( |
777 | | - StandardCharsets.UTF_8)))); |
| 777 | + try { |
| 778 | + if (container instanceof ClientManager client) { |
| 779 | + |
| 780 | + String proxyHost = System.getProperty("http.proxyHost", System.getenv("proxy_host")); |
| 781 | + String proxyPort = System.getProperty("http.proxyPort"); |
| 782 | + if (proxyHost != null |
| 783 | + && "http".equals(hudsonUrl.getProtocol()) |
| 784 | + && NoProxyEvaluator.shouldProxy(hudsonUrl.getHost())) { |
| 785 | + URI proxyUri; |
| 786 | + if (proxyPort != null) { |
| 787 | + proxyUri = URI.create(String.format("http://%s:%s", proxyHost, proxyPort)); |
| 788 | + } else { |
| 789 | + proxyUri = URI.create(String.format("http://%s", proxyHost)); |
| 790 | + } |
| 791 | + client.getProperties().put(ClientProperties.PROXY_URI, proxyUri); |
| 792 | + if (proxyCredentials != null) { |
| 793 | + client.getProperties() |
| 794 | + .put( |
| 795 | + ClientProperties.PROXY_HEADERS, |
| 796 | + Map.of( |
| 797 | + "Proxy-Authorization", |
| 798 | + "Basic " |
| 799 | + + Base64.getEncoder() |
| 800 | + .encodeToString(proxyCredentials.getBytes( |
| 801 | + StandardCharsets.UTF_8)))); |
| 802 | + } |
778 | 803 | } |
779 | | - } |
780 | 804 |
|
781 | | - if (sslContext != null) { |
782 | | - SslEngineConfigurator sslEngineConfigurator = new SslEngineConfigurator(sslContext); |
783 | | - if (hostnameVerifier != null) { |
784 | | - sslEngineConfigurator.setHostnameVerifier(hostnameVerifier); |
| 805 | + if (sslContext != null) { |
| 806 | + SslEngineConfigurator sslEngineConfigurator = new SslEngineConfigurator(sslContext); |
| 807 | + if (hostnameVerifier != null) { |
| 808 | + sslEngineConfigurator.setHostnameVerifier(hostnameVerifier); |
| 809 | + } |
| 810 | + client.getProperties().put(ClientProperties.SSL_ENGINE_CONFIGURATOR, sslEngineConfigurator); |
785 | 811 | } |
786 | | - client.getProperties().put(ClientProperties.SSL_ENGINE_CONFIGURATOR, sslEngineConfigurator); |
| 812 | + } |
| 813 | + if (!succeedsWithRetries(() -> this.pingSuccessful(sslContext))) { |
| 814 | + return; |
| 815 | + } |
| 816 | + if (!succeedsWithRetries(() -> { |
| 817 | + container.connectToServer( |
| 818 | + new AgentEndpoint(), |
| 819 | + ClientEndpointConfig.Builder.create() |
| 820 | + .configurator(headerHandler) |
| 821 | + .build(), |
| 822 | + URI.create(wsUrl + "wsagents/")); |
| 823 | + return true; |
| 824 | + })) { |
| 825 | + return; |
| 826 | + } |
| 827 | + while (ch.get() == null) { |
| 828 | + Thread.sleep(100); |
| 829 | + } |
| 830 | + this.protocolName = "WebSocket"; |
| 831 | + events.status("Connected"); |
| 832 | + ch.get().join(); |
| 833 | + events.status("Terminated"); |
| 834 | + if (noReconnect) { |
| 835 | + return; |
| 836 | + } |
| 837 | + events.onDisconnect(); |
| 838 | + reconnect(); |
| 839 | + } finally { |
| 840 | + if (container instanceof BaseContainer baseContainer) { |
| 841 | + baseContainer.shutdown(); |
787 | 842 | } |
788 | 843 | } |
789 | | - if (!succeedsWithRetries(() -> this.pingSuccessful(sslContext))) { |
790 | | - return; |
791 | | - } |
792 | | - if (!succeedsWithRetries(() -> { |
793 | | - container.connectToServer( |
794 | | - new AgentEndpoint(), |
795 | | - ClientEndpointConfig.Builder.create() |
796 | | - .configurator(headerHandler) |
797 | | - .build(), |
798 | | - URI.create(wsUrl + "wsagents/")); |
799 | | - return true; |
800 | | - })) { |
801 | | - return; |
802 | | - } |
803 | | - while (ch.get() == null) { |
804 | | - Thread.sleep(100); |
805 | | - } |
806 | | - this.protocolName = "WebSocket"; |
807 | | - events.status("Connected"); |
808 | | - ch.get().join(); |
809 | | - events.status("Terminated"); |
810 | | - if (noReconnect) { |
811 | | - return; |
812 | | - } |
813 | | - events.onDisconnect(); |
814 | | - reconnect(); |
815 | 844 | } |
816 | 845 | } catch (Exception e) { |
817 | 846 | events.error(e); |
|
0 commit comments