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

org.wisdom.framework.vertx.WisdomVertxServer Maven / Gradle / Ivy

There is a newer version: 0.10.0
Show newest version
/*
 * #%L
 * Wisdom-Framework
 * %%
 * Copyright (C) 2013 - 2014 Wisdom Framework
 * %%
 * Licensed 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.
 * #L%
 */
package org.wisdom.framework.vertx;

import org.apache.felix.ipojo.annotations.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.vertx.java.core.Vertx;
import org.wisdom.api.concurrent.ManagedExecutorService;
import org.wisdom.api.configuration.ApplicationConfiguration;
import org.wisdom.api.configuration.Configuration;
import org.wisdom.api.content.ContentEngine;
import org.wisdom.api.crypto.Crypto;
import org.wisdom.api.engine.WisdomEngine;
import org.wisdom.api.exceptions.ExceptionMapper;
import org.wisdom.api.http.websockets.WebSocketDispatcher;
import org.wisdom.api.http.websockets.WebSocketListener;
import org.wisdom.api.router.Router;

import java.net.InetAddress;
import java.util.*;


/**
 * The main entry point of the Vert.x engine for Wisdom. This component is responsible for the creation of the
 * different server and their configuration. It also exposed the {@link org.wisdom.api.engine.WisdomEngine} and
 * {@link org.wisdom.api.http.websockets.WebSocketDispatcher} services.
 */
@Component
@Provides
@Instantiate
public class WisdomVertxServer implements WebSocketDispatcher, WisdomEngine {


    private static final Logger LOGGER = LoggerFactory.getLogger(WisdomVertxServer.class);

    /**
     * The set of Web Socket Listeners used to dispatch data received on web sockets.
     */
    private List listeners = new ArrayList<>();

    /**
     * The map of uri / list of channel context keeping a reference on all opened web sockets.
     */
    private Map> socketsByUri = new HashMap<>();

    /**
     * The vertx singleton.
     */
    @Requires
    Vertx vertx;

    /**
     * The router.
     */
    @Requires
    private Router router;

    /**
     * The configuration.
     */
    @Requires
    ApplicationConfiguration configuration;

    /**
     * The crypto service.
     */
    @Requires
    private Crypto crypto;

    /**
     * The content engine.
     */
    @Requires
    private ContentEngine engine;

    /**
     * The thread pool used by the server (system).
     */
    @Requires(filter = "(name=" + ManagedExecutorService.SYSTEM + ")")
    private ManagedExecutorService executor;

    /**
     * The exception mappers.
     */
    @Requires(specification = ExceptionMapper.class, optional = true)
    private Collection mappers;

    /**
     * The accessor to get all the services.
     */
    ServiceAccessor accessor = new ServiceAccessor(crypto, configuration, router,
            engine, executor, this, mappers); //NOSONAR

    private InetAddress address;

    protected List servers = new ArrayList<>(2);

    /**
     * Starts the servers (HTTP and HTTPS).
     * The actual start is asynchronous.
     */
    @Validate
    public void start() {
        LOGGER.info("Starting the vert.x server");
        // Check whether we have a specific vertx configuration, if not try the global one, and if not use default.
        int httpPort = accessor.getConfiguration().getIntegerWithDefault(
                "vertx.http.port",
                accessor.getConfiguration().getIntegerWithDefault(ApplicationConfiguration.HTTP_PORT, 9000));
        int httpsPort = accessor.getConfiguration().getIntegerWithDefault(
                "vertx.https.port",
                accessor.getConfiguration().getIntegerWithDefault(ApplicationConfiguration.HTTPS_PORT, -1));

        initializeInetAddress();

        // Parse server configuration if any
        Configuration servers = configuration.getConfiguration("vertx.servers");
        if (servers == null) {
            if (httpPort != -1) {
                LOGGER.info("Configuring default HTTP Server");
                this.servers.add(Server.defaultHttp(accessor, vertx));
            }
            if (httpsPort != -1) {
                LOGGER.info("Configuring default HTTPS Server");
                this.servers.add(Server.defaultHttps(accessor, vertx));
            }
        } else {
            // Custom configuration
            for (String name : servers.asMap().keySet()) {
                LOGGER.info("Configuring server {}", name);
                this.servers.add(Server.from(accessor, vertx, name,
                        servers.getConfiguration(name)));
            }
        }

        for (Server conf : this.servers) {
            conf.bind();
        }
    }

    private void initializeInetAddress() {
        address = null;
        try {
            if (accessor.getConfiguration().get("http.address") != null) {
                address = InetAddress.getByName(accessor.getConfiguration().get("http.address"));
            }
        } catch (Exception e) {
            LOGGER.error("Could not understand http.address", e);
            onError();
        }
    }

    private void onError() {
        System.exit(-1); //NOSONAR
    }

    /**
     * Stops the different servers.
     */
    @Invalidate
    public void stop() {
        listeners.clear();
        LOGGER.info("Stopping the vert.x server");

        for (Server configuration : servers) {
            configuration.close();
        }
    }

    /**
     * @return the hostname.
     */
    public String hostname() {
        if (address == null) {
            return "localhost";
        } else {
            return address.getHostName();
        }
    }

    /**
     * @return the HTTP port on which the current HTTP server is bound. {@literal -1} means that the HTTP connection
     * is not enabled.
     */
    public synchronized int httpPort() {
        for (Server server : servers) {
            if (!server.ssl()) {
                return server.port();
            }
        }
        return -1;
    }

    /**
     * @return the HTTP port on which the current HTTPS server is bound. {@literal -1} means that the HTTPS connection
     * is not enabled.
     */
    public synchronized int httpsPort() {
        for (Server server : servers) {
            if (server.ssl()) {
                return server.port();
            }
        }
        return -1;
    }

    /**
     * Publishes the given message to all clients subscribed to the socket (either a web socket of a SockJS socket)
     * specified using its url. For SockJS, it must match one of the configured prefix.
     *
     * @param url  the url of the web socket, must not be {@literal null}
     * @param data the data, must not be {@literal null}
     */
    @Override
    public void publish(String url, String data) {
        List sockets;
        synchronized (this) {
            List ch = this.socketsByUri.get(url);
            if (ch != null) {
                sockets = new ArrayList<>(ch);
            } else {
                sockets = Collections.emptyList();
            }
        }
        for (Socket socket : sockets) {
            socket.publish(data, vertx.eventBus());
        }
    }

    /**
     * Publishes the given message to all clients subscribed to the socket ((either a web socket of a SockJS socket))
     * specified using its url. For SockJS, it must match one of the configured prefix.
     *
     * @param url  the url of the socket, must not be {@literal null}
     * @param data the data, must not be {@literal null}
     */
    @Override
    public synchronized void publish(String url, byte[] data) {
        List sockets;
        synchronized (this) {
            List ch = this.socketsByUri.get(url);
            if (ch != null) {
                sockets = new ArrayList<>(ch);
            } else {
                sockets = Collections.emptyList();
            }
        }
        for (Socket socket : sockets) {
            socket.publish(data, vertx.eventBus());
        }
    }

    /**
     * A client subscribed to a socket (either a web socket of a SockJS socket).
     *
     * @param url    the url of the web sockets.
     * @param socket the client channel.
     */
    public void addSocket(String url, Socket socket) {
        LOGGER.info("Adding web socket on {} bound to {}", url, socket);
        List webSocketListeners;
        synchronized (this) {
            List channels = socketsByUri.get(url);
            if (channels == null) {
                channels = new ArrayList<>();
            }
            channels.add(socket);
            socketsByUri.put(url, channels);
            webSocketListeners = new ArrayList<>(this.listeners);
        }

        for (WebSocketListener listener : webSocketListeners) {
            listener.opened(url, id(socket));
        }
    }

    /**
     * A client disconnected from a socket (either a web socket of a SockJS socket).
     *
     * @param url    the url of the web sockets.
     * @param socket the client channel.
     */
    public void removeSocket(String url, Socket socket) {
        LOGGER.info("Removing web socket on {} bound to {}", url, socket.path());
        List webSocketListeners;
        synchronized (this) {
            List channels = socketsByUri.get(url);
            if (channels != null) {
                channels.remove(socket);
                if (channels.isEmpty()) {
                    socketsByUri.remove(url);
                }
            }
            webSocketListeners = new ArrayList<>(this.listeners);
        }

        for (WebSocketListener listener : webSocketListeners) {
            listener.closed(url, id(socket));
        }
    }

    /**
     * Registers a WebSocketListener. The listener will receive a 'open' notification for all clients connected to
     * web sockets.
     *
     * @param listener the listener, must not be {@literal null}
     */
    @Override
    public void register(WebSocketListener listener) {
        Map> copy;
        synchronized (this) {
            listeners.add(listener);
            copy = new HashMap<>(socketsByUri);
        }

        // Call open on each opened web socket
        for (Map.Entry> entry : copy.entrySet()) {
            for (Socket client : entry.getValue()) {
                listener.opened(entry.getKey(), id(client));
            }
        }
    }

    /**
     * Un-registers a web socket listeners.
     *
     * @param listener the listener, must not be {@literal null}
     */
    @Override
    public void unregister(WebSocketListener listener) {
        synchronized (this) {
            listeners.remove(listener);
        }
    }

    /**
     * Sends the given message to the client identify by its id and listening to the websocket having the given url.
     *
     * @param uri     the web socket url
     * @param client  the client id, retrieved from the {@link org.wisdom.api.http.websockets.WebSocketListener#opened
     *                (String, String)} method.
     * @param message the message to send
     */
    @Override
    public void send(String uri, String client, String message) {
        List sockets;
        synchronized (this) {
            List ch = this.socketsByUri.get(uri);
            if (ch != null) {
                sockets = new ArrayList<>(ch);
            } else {
                sockets = Collections.emptyList();
            }
        }
        for (Socket socket : sockets) {
            if (client.equals(id(socket))) {
                socket.publish(message, vertx.eventBus());
            }
        }
    }

    /**
     * Computes the client id for the given {@link org.wisdom.framework.vertx.Socket}.
     *
     * @param socket the socket.
     * @return the id
     */
    static String id(Socket socket) {
        return Integer.toOctalString(socket.hashCode());
    }

    /**
     * Sends the given message to the client identify by its id and listening to the websocket having the given url.
     *
     * @param uri     the web socket url
     * @param client  the client id, retrieved from the {@link org.wisdom.api.http.websockets.WebSocketListener#opened
     *                (String, String)} method.
     * @param message the message to send
     */
    @Override
    public void send(String uri, String client, byte[] message) {
        List sockets;
        synchronized (this) {
            List ch = this.socketsByUri.get(uri);
            if (ch != null) {
                sockets = new ArrayList<>(ch);
            } else {
                sockets = Collections.emptyList();
            }
        }
        for (Socket socket : sockets) {
            if (client.equals(id(socket))) {
                socket.publish(message, vertx.eventBus());
            }
        }
    }

    /**
     * Method called when some data is received on a web socket. It delegates to the registered listeners.
     *
     * @param uri     the web socket url
     * @param content the data
     * @param socket  the client channel
     */
    public void received(String uri, byte[] content, Socket socket) {
        List localListeners;
        synchronized (this) {
            localListeners = new ArrayList<>(this.listeners);
        }

        for (WebSocketListener listener : localListeners) {
            listener.received(uri, id(socket), content);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy