All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.glassfish.tyrus.ext.client.java8.SessionBuilder Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2015, 2021 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.ext.client.java8;

import java.io.IOException;
import java.net.URI;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.function.BiConsumer;

import jakarta.websocket.ClientEndpointConfig;
import jakarta.websocket.CloseReason;
import jakarta.websocket.DeploymentException;
import jakarta.websocket.Endpoint;
import jakarta.websocket.EndpointConfig;
import jakarta.websocket.MessageHandler;
import jakarta.websocket.Session;
import jakarta.websocket.WebSocketContainer;

import org.glassfish.tyrus.client.ClientManager;
import org.glassfish.tyrus.core.Beta;
import org.glassfish.tyrus.core.MessageHandlerManager;

/**
 * Session builder removed the need for having client endpoint.
 * 

* Client endpoint is replaced by references to onOpen, onError and onClose methods plus message handlers. Same rules * which do apply or limit message handlers are forced here as well. None of the methods is required, so {@link * Session} * can be opened even without it and used only for sending messages. *

* {@link jakarta.websocket.Encoder Encoders} and {@link jakarta.websocket.Decoder decoders} can be registered by creating * {@link jakarta.websocket.ClientEndpointConfig} and registering it to SessionBuilder via * {@link org.glassfish.tyrus.ext.client.java8.SessionBuilder#clientEndpointConfig} method call. *

* Code example: *

Session session = new SessionBuilder()
 *      .uri(getURI(SessionBuilderEncDecTestEndpoint.class))
 *      .clientEndpointConfig(clientEndpointConfig)
 *      .messageHandler(AClass.class,aClass -> messageLatch.countDown())
 *      .onOpen((session1, endpointConfig) -> onOpenLatch.countDown())
 *      .onError((session1, throwable) -> onErrorLatch.countDown())
 *      .onClose((session1, closeReason) -> onCloseLatch.countDown())
 *      .connect();
* * @author Pavel Bucek */ @Beta public class SessionBuilder { private static final BiConsumer NO_OP_BI_CONSUMER = (o, o2) -> { // no-op }; private final WebSocketContainer container; private final List, MessageHandler.Whole>> wholeMessageHandlers = new ArrayList<>(); private final List, MessageHandler.Partial>> partialMessageHandlers = new ArrayList<>(); private URI uri; private ClientEndpointConfig clientEndpointConfig; private BiConsumer onOpen; private BiConsumer onError; private BiConsumer onClose; /** * Create SessionBuilder with provided {@link jakarta.websocket.WebSocketContainer}. * * @param container provided websocket container. */ public SessionBuilder(WebSocketContainer container) { this.container = container; } /** * Create SessionBuilder with provided container provider class name. *

* Generally, this is used only when you want to have fine-grained control about used container. * * @param containerProviderClassName container provider class name. */ public SessionBuilder(String containerProviderClassName) { this(ClientManager.createClient(containerProviderClassName)); } /** * Create new SessionBuilder instance. */ public SessionBuilder() { this(ClientManager.createClient()); } /** * Set {@link jakarta.websocket.ClientEndpointConfig}. * * @param clientEndpointConfig {@link jakarta.websocket.ClientEndpointConfig} to be set. * @return updated SessionBuilder instance. */ public SessionBuilder clientEndpointConfig(ClientEndpointConfig clientEndpointConfig) { this.clientEndpointConfig = clientEndpointConfig; return this; } /** * Set {@link java.net.URI} of the server endpoint. * * @param uri server endpoint address. * @return updated SessionBuilder instance. */ public SessionBuilder uri(URI uri) { this.uri = uri; return this; } /** * Add whole message handler. * * @param clazz handled message type class. * @param messageHandler message handler. * @param handled message type. * @return updated SessionBuilder instance. */ public SessionBuilder messageHandler(Class clazz, MessageHandler.Whole messageHandler) { wholeMessageHandlers.add(new AbstractMap.SimpleEntry<>(clazz, messageHandler)); return this; } /** * Add partial message handler. * * @param clazz handled message type class. * @param messageHandler message handler. * @param handled message type. * @return updated SessionBuilder instance. */ public SessionBuilder messageHandlerPartial(Class clazz, MessageHandler.Partial messageHandler) { partialMessageHandlers.add(new AbstractMap.SimpleEntry<>(clazz, messageHandler)); return this; } /** * Set method reference which will be invoked when a {@link Session} is opened. * * @param onOpen method invoked when a {@link Session} is opened. * @return updated SessionBuilder instance. * @see jakarta.websocket.OnOpen */ public SessionBuilder onOpen(BiConsumer onOpen) { this.onOpen = onOpen; return this; } /** * Set method reference which will be invoked when {@link jakarta.websocket.OnError} method is invoked. * * @param onError method invoked when {@link jakarta.websocket.OnError} method is invoked. * @return updated SessionBuilder instance. * @see jakarta.websocket.OnError */ public SessionBuilder onError(BiConsumer onError) { this.onError = onError; return this; } /** * Set method reference which will be invoked when a {@link Session} is closed. * * @param onClose method invoked when a {@link Session} is closed. * @return updated SessionBuilder instance. * @see jakarta.websocket.OnClose */ public SessionBuilder onClose(BiConsumer onClose) { this.onClose = onClose; return this; } /** * Connect to the remote (server) endpoint. *

* This method can be called multiple times, each invocation will result in new {@link Session} (new TCP connection * to the server). * * @return created session. * @throws IOException when there is a problem with connecting to the server endpoint. * @throws DeploymentException when there is a problem with provided settings or there is other, non IO connection * issue. */ public Session connect() throws IOException, DeploymentException { // default values if (clientEndpointConfig == null) { clientEndpointConfig = ClientEndpointConfig.Builder.create().build(); } //noinspection unchecked onOpen = onOpen != null ? onOpen : (BiConsumer) NO_OP_BI_CONSUMER; //noinspection unchecked onClose = onClose != null ? onClose : (BiConsumer) NO_OP_BI_CONSUMER; //noinspection unchecked onError = onError != null ? onError : (BiConsumer) NO_OP_BI_CONSUMER; // validation MessageHandlerManager messageHandlerManager = MessageHandlerManager.fromDecoderClasses(clientEndpointConfig.getDecoders()); try { for (Map.Entry, MessageHandler.Whole> entry : wholeMessageHandlers) { messageHandlerManager .addMessageHandler((Class) entry.getKey(), (MessageHandler.Whole) entry.getValue()); } for (Map.Entry, MessageHandler.Partial> entry : partialMessageHandlers) { messageHandlerManager .addMessageHandler((Class) entry.getKey(), (MessageHandler.Partial) entry.getValue()); } } catch (IllegalStateException ise) { throw new DeploymentException(ise.getMessage(), ise); } // validation end final URI path = this.uri; final ClientEndpointConfig clientEndpointConfig = this.clientEndpointConfig; final BiConsumer onOpen = this.onOpen; final BiConsumer onError = this.onError; final BiConsumer onClose = this.onClose; final Endpoint endpoint = new Endpoint() { @Override public void onOpen(Session session, EndpointConfig config) { for (Map.Entry, MessageHandler.Whole> entry : wholeMessageHandlers) { session.addMessageHandler((Class) entry.getKey(), (MessageHandler.Whole) entry.getValue()); } for (Map.Entry, MessageHandler.Partial> entry : partialMessageHandlers) { session.addMessageHandler((Class) entry.getKey(), (MessageHandler.Partial) entry.getValue()); } onOpen.accept(session, config); } @Override public void onClose(Session session, CloseReason closeReason) { onClose.accept(session, closeReason); } @Override public void onError(Session session, Throwable thr) { onError.accept(session, thr); } }; return container.connectToServer(endpoint, clientEndpointConfig, path); } /** * Connect to the remote (server) endpoint asynchronously. *

* Same statements as at {@link SessionBuilder#connect()} do apply here, only the returned value and possible * exceptions are returned as {@link java.util.concurrent.CompletableFuture}. *

* {@link ForkJoinPool#commonPool()} is used for executing the connection phase. * * @return completable future returning {@link Session} when created. */ public CompletableFuture connectAsync() { final CompletableFuture completableFuture = new CompletableFuture<>(); final ForkJoinTask forkJoinTask = new ForkJoinTask() { @Override public final Void getRawResult() { return null; } @Override public final void setRawResult(Void v) { } @Override protected boolean exec() { try { completableFuture.complete(connect()); return true; } catch (Exception e) { completableFuture.completeExceptionally(e); } return false; } }; // TODO: Can we use ForkJoinPool#commonPool? ForkJoinPool.commonPool().execute(forkJoinTask); //noinspection unchecked return completableFuture; } /** * Connect to the remote (server) endpoint asynchronously. *

* Same statements as at {@link SessionBuilder#connect()} do apply here, only the returned value and possible * exceptions are returned as {@link java.util.concurrent.CompletableFuture}. *

* Provided {@link java.util.concurrent.ExecutorService} is used for executing the connection phase. * * @param executorService executor service used for executing the {@link #connect()} method. * @return completable future returning {@link Session} when created. */ public CompletableFuture connectAsync(ExecutorService executorService) { final CompletableFuture completableFuture = new CompletableFuture<>(); Runnable runnable = () -> { try { completableFuture.complete(connect()); } catch (Exception e) { completableFuture.completeExceptionally(e); } }; executorService.execute(runnable); return completableFuture; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy