001/*
002 * Copyright 2013 Atteo.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.atteo.moonshine.websocket.jsonmessages;
018
019import java.io.IOException;
020import java.net.URI;
021import java.net.URISyntaxException;
022
023import javax.websocket.ClientEndpointConfig;
024import javax.websocket.ContainerProvider;
025import javax.websocket.DeploymentException;
026import javax.websocket.OnMessage;
027import javax.websocket.Session;
028
029import org.atteo.moonshine.webserver.WebServerAddress;
030
031import com.fasterxml.jackson.annotation.JsonTypeName;
032
033/**
034 * Client side JSON messages support.
035 */
036public class JsonMessages {
037    public interface Factory {
038        /**
039         * Adds handler.
040         * <p>
041         * Handler is an object with methods annotated with {@link OnMessage} annotation.
042         * Each such method must take exactly one argument whose type should implement
043         * {@link JsonMessage} interface and be annotated with {@link JsonTypeName}.
044         * Such method will be called when message of matching type will be received.
045         * Optionally handler method can return {@link JsonMessage} object which will be then
046         * sent back to the message sender.
047         * </p>
048         */
049        <T> Factory addHandler(T handler);
050        /**
051         * Adds message sender.
052         * <p>
053         * Message sender allows you to send messages through WebSocket. The approach here is similiar
054         * to that of Spring Data. You provide an interface with sender methods. Each such method
055         * should take exactly one argument whose type should implement {@link JsonMessage} interface
056         * and be annotated with {@link JsonTypeName}. The returned object {@link SenderProvider} allows
057         * you to retrieve implementation of the interface for given {@link Session}.
058         * You can then call sender methods to send given message.
059         * </p>
060         */
061        <T> SenderProvider<T> addSender(Class<T> senderClass);
062        /**
063         * Connect to the specified uri.
064         * <p>
065         * After the connection is established any registered handlers can be called when matching
066         * message will be received through this Web Socket.
067         * </p>
068         */
069        Session connect(URI uri);
070        /**
071         * Connect to the specified web server.
072         * <p>
073         * This is helper method mainly for use in tests which calls
074         * {@link #connect(URI)} with the address derived from given {@link WebServerAddress web server address}.
075         * </p>
076         */
077        Session connect(WebServerAddress webServerAddress) throws URISyntaxException;
078    }
079
080    public static Factory factory() {
081        final HandlerDispatcher dispatcher = new HandlerDispatcher();
082        return new Factory() {
083            @Override
084            public <T> Factory addHandler(T handler) {
085                dispatcher.addHandler(handler);
086                return this;
087            }
088
089            @Override
090            public <T> SenderProvider<T> addSender(Class<T> senderClass) {
091                return dispatcher.addSender(senderClass);
092            }
093
094            @Override
095            public Session connect(URI uri) {
096                ClientEndpointConfig config = ClientEndpointConfig.Builder.create().build();
097
098                try {
099                    return ContainerProvider.getWebSocketContainer().connectToServer(
100                            new JsonMessagesEndpoint(dispatcher), config, uri);
101                } catch (DeploymentException | IOException ex) {
102                    throw new RuntimeException(ex);
103                }
104            }
105
106            @Override
107            public Session connect(WebServerAddress webServerAddress) throws URISyntaxException {
108                String path = "/json-messages";
109                URI uri =  new URI(webServerAddress.getUrl().replace("http://", "ws://") + path);
110                return connect(uri);
111            }
112        };
113    }
114
115}