br.com.criativasoft.opendevice.wsrest.AbstractAtmosphereConnection.java Source code

Java tutorial

Introduction

Here is the source code for br.com.criativasoft.opendevice.wsrest.AbstractAtmosphereConnection.java

Source

/*
 * ******************************************************************************
 *  Copyright (c) 2013-2014 CriativaSoft (www.criativasoft.com.br)
 *  All rights reserved. This program and the accompanying materials
 *  are made available under the terms of the Eclipse Public License v1.0
 *  which accompanies this distribution, and is available at
 *  http://www.eclipse.org/legal/epl-v10.html
 *
 *  Contributors:
 *  Ricardo JL Rufino - Initial API and Implementation
 * *****************************************************************************
 */

package br.com.criativasoft.opendevice.wsrest;

import br.com.criativasoft.opendevice.connection.*;
import br.com.criativasoft.opendevice.connection.exception.ConnectionException;
import br.com.criativasoft.opendevice.connection.message.Message;
import br.com.criativasoft.opendevice.connection.message.Request;
import br.com.criativasoft.opendevice.core.DeviceManager;
import br.com.criativasoft.opendevice.core.ODev;
import br.com.criativasoft.opendevice.core.TenantProvider;
import br.com.criativasoft.opendevice.core.command.*;
import br.com.criativasoft.opendevice.core.connection.ConnectionInfo;
import br.com.criativasoft.opendevice.core.connection.ConnectionType;
import br.com.criativasoft.opendevice.core.model.OpenDeviceConfig;
import br.com.criativasoft.opendevice.core.util.StringUtils;
import br.com.criativasoft.opendevice.restapi.WaitResponseListener;
import br.com.criativasoft.opendevice.restapi.auth.AccountDaoRealm;
import br.com.criativasoft.opendevice.restapi.auth.BearerAuthRealm;
import br.com.criativasoft.opendevice.restapi.auth.GoogleAuthRealm;
import br.com.criativasoft.opendevice.restapi.resources.DeviceRest;
import br.com.criativasoft.opendevice.wsrest.filter.CrossOriginInterceptor;
import br.com.criativasoft.opendevice.wsrest.filter.NewShiroInterceptor;
import br.com.criativasoft.opendevice.wsrest.guice.config.ConnectionGuiceProvider;
import br.com.criativasoft.opendevice.wsrest.guice.config.DeviceManagerGuiceProvider;
import br.com.criativasoft.opendevice.wsrest.io.JacksonProvider;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.Authenticator;
import org.apache.shiro.authc.pam.FirstSuccessfulStrategy;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.atmosphere.cpr.*;
import org.atmosphere.nettosphere.Config;
import org.atmosphere.nettosphere.Nettosphere;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.*;

/**
 * Base WebSocket/REST/HTTP Server Connection
 * @author Ricardo JL Rufino
 * @date 11/06/2013
 */
public abstract class AbstractAtmosphereConnection extends AbstractConnection
        implements AtmosphereInterceptor, ConnectionListener, ServerConnection {

    private static final Logger log = LoggerFactory.getLogger(AbstractAtmosphereConnection.class);

    private int port;

    private Nettosphere server;

    private BroadcasterFactory broadcasterFactory;

    private List<String> webresources = new LinkedList<String>();
    private List<Class<?>> resources = new LinkedList<Class<?>>();

    private List<WaitResponseListener> waitListeners = new LinkedList<WaitResponseListener>();

    public AbstractAtmosphereConnection() {
        super();
    }

    public AbstractAtmosphereConnection(int port) {
        super();
        this.port = port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    @Override
    public void connect() throws ConnectionException {
        try {
            log.debug("connecting...");
            if (!isConnected()) {

                initConnection(); // Setup

                initServerEvents();

                log.debug("Starting server on port: " + port);
                server.start();

                setStatus(ConnectionStatus.CONNECTED);

            }

        } catch (IOException e) {
            throw new ConnectionException(e);
        }

    }

    private void initConnection() throws IOException {
        if (server == null) {
            OpenDeviceConfig odevc = ODev.getConfig();

            Config.Builder conf = new Config.Builder();
            conf.port(port);
            conf.supportChunking(true);
            conf.maxChunkContentLength(5 * 1024 * 1024); // 5BM

            //conf.host("::0"); // bind all local IPs
            conf.host("0.0.0.0"); // bind all local IPs
            configure(conf);

            conf.resource(JacksonProvider.class);

            // Custom static resources
            for (String resource : webresources) {
                conf.resource(resource);
            }

            // Jersey
            for (Class<?> resource : resources) {
                conf.resource(resource);
            }

            conf.initParam("com.sun.jersey.api.json.POJOMappingFeature", "true");
            conf.initParam(ApplicationConfig.BROADCASTER_MESSAGE_PROCESSING_THREADPOOL_MAXSIZE, "10");
            conf.initParam(ApplicationConfig.BROADCASTER_ASYNC_WRITE_THREADPOOL_MAXSIZE, "10");
            conf.initParam(ApplicationConfig.SCAN_CLASSPATH, "false");
            conf.initParam(ApplicationConfig.ANALYTICS, "false");
            // conf.initParam(ApplicationConfig.DROP_ACCESS_CONTROL_ALLOW_ORIGIN_HEADER, "false");

            // conf.initParam("com.sun.jersey.spi.container.ResourceMethodDispatchProvider", "true");
            //.initParam(ApplicationConfig.OBJECT_FACTORY, GuiceConfigFactory.class.getName())
            conf.interceptor(new CrossOriginInterceptor());
            if (odevc.isAuthRequired())
                conf.interceptor(new NewShiroInterceptor());
            //            conf.interceptor(new JacksonFilterInterceptor());
            conf.interceptor(this); // add this as interceptor

            // SSL Support
            String certificate = odevc.getCertificateFile();
            if (!StringUtils.isEmpty(certificate)) {
                //                File cert = new File(certificate);
                //                if(!cert.exists()) throw new IllegalArgumentException("Certificate not found !");
                //                File key = new File(odevc.getCertificateKey());
                //                if(!key.exists()) throw new IllegalArgumentException("Certificate key must be provided !");
                //
                //                SslContext sslContext = SslContext.newServerContext(SslProvider.JDK, cert, key, odevc.getCertificatePass());
                //                conf.sslContext(sslContext);
            }

            // Authentication
            if (odevc.isAuthRequired()) {
                List<Realm> realms = new LinkedList<Realm>();
                realms.add(new BearerAuthRealm((DeviceManager) getConnectionManager()));
                realms.add(new GoogleAuthRealm((DeviceManager) getConnectionManager()));
                realms.add(new AccountDaoRealm((DeviceManager) getConnectionManager()));

                RestWebSecurityManager securityManager = new RestWebSecurityManager(realms);
                securityManager.setCacheManager(new MemoryConstrainedCacheManager());
                securityManager.setSessionManager(new DefaultWebSessionManager());

                Authenticator authenticator = securityManager.getAuthenticator();
                if (authenticator instanceof ModularRealmAuthenticator) {
                    ((ModularRealmAuthenticator) authenticator)
                            .setAuthenticationStrategy(new FirstSuccessfulStrategy());
                }

                // NOTE: Works with ShiroResourceFilterFactory, registred in AppResourceConfigurator
                SecurityUtils.setSecurityManager(securityManager);

            }

            server = new Nettosphere.Builder().config(conf.build()).build();

            broadcasterFactory = server.framework().getBroadcasterFactory();
        }
    }

    protected OpenDeviceConfig getConfig() {
        return OpenDeviceConfig.get();
    }

    protected abstract void configure(Config.Builder conf);

    @Override
    public void disconnect() throws ConnectionException {
        if (isConnected()) {
            server.stop();
        }
    }

    @Override
    public boolean isConnected() {
        return server != null && ConnectionStatus.CONNECTED.equals(getStatus());
    }

    @Override
    public void send(Message message) throws IOException {

        // Notify clients that are waiting for a response
        Iterator<WaitResponseListener> iterator = waitListeners.iterator();
        while (iterator.hasNext()) {
            WaitResponseListener waitListener = iterator.next();
            boolean accept = waitListener.accept(message);
            if (accept)
                iterator.remove();
        }

        broadcast(message);
    }

    private void initServerEvents() {
        addListener(this);
    }

    public void addWebResource(String path) {
        if (path != null) {
            webresources.add(path);
        }
    }

    public void addResource(Class<?> resource) {
        if (resource != null) {
            resources.add(resource);
        }
    }

    @Override
    public void configure(AtmosphereConfig atmosphereConfig) {
        //System.out.println("WSServerConnection :: configure");
        //ServletContext servletContext = atmosphereConfig.getServletConfig().getServletContext();
    }

    @Override
    public Action inspect(AtmosphereResource atmosphereResource) {

        AtmosphereRequest request = atmosphereResource.getRequest();
        String appID = request.getHeader(TenantProvider.HTTP_HEADER_KEY);
        if (appID != null)
            TenantProvider.setCurrentID(appID);

        DeviceManager manager = (DeviceManager) this.getConnectionManager();
        ConnectionGuiceProvider.setConnection(this);
        DeviceManagerGuiceProvider.setInstance(manager);

        //        DefaultWebSubjectContext subjectContext=new DefaultWebSubjectContext();
        //        subjectContext.setServletRequest(request);
        //        SecurityUtils.getSecurityManager().createSubject(subjectContext);

        // atmosphereResource.getRequest().getParameterMap().put("requestUID", new String[]{atmosphereResource.uuid()});
        // atmosphereResource.getRequest().setAttribute("requestUID", atmosphereResource.uuid());
        // AtmosphereResource atmosphereHandler = atmosphereResource.getAtmosphereHandler();

        return Action.CONTINUE;
    }

    @Override
    public void postInspect(AtmosphereResource atmosphereResource) {
        ConnectionGuiceProvider.setConnection(null);
        DeviceManagerGuiceProvider.setInstance(null);
    }

    @Override
    public void connectionStateChanged(DeviceConnection connection, ConnectionStatus status) {

    }

    @Override
    public Message notifyAndWait(Request request) {

        WaitResponseListener waitResponse = new WaitResponseListener(request, this);
        waitListeners.add(waitResponse);

        try {
            return waitResponse.getResponse(1000);
        } catch (InterruptedException e) {
            log.error(e.getMessage());
            return null;
        }

    }

    /**
     * Used to broadcast events/commands.</br>
     * Is fired by {@linkplain DeviceRest} AND {@linkplain WebSocketResource}
     *
     * @param message
     * @param connection
     */
    @Override
    public void onMessageReceived(Message message, DeviceConnection connection) {

        if (message instanceof Command) {

            Command cmd = (Command) message;

            // Avoid broadcast DeviceCommand twice..
            if (CommandType.allowBroadcast(cmd.getType()) && !DeviceCommand.isCompatible(cmd)) {
                broadcast(message);
            }

        }

    }

    private void broadcast(Message message) {

        if (server == null)
            return;

        if (message instanceof Command) {

            Command cmd = (Command) message;

            // Get broadcast group for client.
            Broadcaster broadcaster;
            if (getConfig().isTenantsEnabled()) {
                broadcaster = broadcasterFactory.lookup(cmd.getApplicationID());
            } else {
                broadcaster = broadcasterFactory.lookup(OpenDeviceConfig.LOCAL_APP_ID);
            }

            if (broadcaster != null) {

                if (log.isDebugEnabled())
                    log.debug("To: " + cmd.getApplicationID() + " ( clients: "
                            + broadcaster.getAtmosphereResources().size() + " ) ");

                Collection<AtmosphereResource> atmosphereResources = broadcaster.getAtmosphereResources();
                //                System.out.println("WSServer.clients = "+ atmosphereResources.size());

                for (AtmosphereResource atmosphereResource : atmosphereResources) {

                    if (cmd instanceof ResponseCommand && !(cmd instanceof GetDevicesResponse)) {

                        if (atmosphereResource.uuid().equals(cmd.getConnectionUUID())) {
                            broadcaster.broadcast(message, atmosphereResource);
                        }

                    } else if (!atmosphereResource.uuid().equals(cmd.getConnectionUUID())) {
                        broadcaster.broadcast(message, atmosphereResource);
                    }

                }

            } else {
                log.warn("To: " + cmd.getApplicationID() + "( broadcast channel not found )");
            }

        }
    }

    public List<ConnectionInfo> getConnections() {
        String appID = TenantProvider.getCurrentID();

        Collection<Broadcaster> broadcasters = broadcasterFactory.lookupAll();

        List<ConnectionInfo> resources = new LinkedList();

        for (Broadcaster broadcaster : broadcasters) {
            Collection<AtmosphereResource> atmosphereResources = broadcaster.getAtmosphereResources();
            for (AtmosphereResource atmosphereResource : atmosphereResources) {
                Object message = atmosphereResource.getAtmosphereResourceEvent().getMessage();
                AtmosphereRequest request = atmosphereResource.getRequest();
                if (request.getPathInfo().contains("/ws/device")) {
                    Object lastConnectionDate = request.getAttribute("lastConnectionDate");
                    ConnectionInfo info = new ConnectionInfo();
                    info.setHost(request.getRemoteHost() + ":" + request.getRemotePort());
                    info.setType(ConnectionType.WEBSOCKET.name());
                    info.setApplicationID(appID);
                    info.setUuid(atmosphereResource.uuid());
                    info.setLastConnection((lastConnectionDate != null ? (Date) lastConnectionDate : null));
                    info.setFistConnection((lastConnectionDate != null ? (Date) lastConnectionDate : null));
                    resources.add(info);
                }
            }
        }

        return resources;
    }

    public List<String> getWebresources() {
        return webresources;
    }

    public void destroy() {

    }

}