com.conwet.xjsp.session.ConnectionContext.java Source code

Java tutorial

Introduction

Here is the source code for com.conwet.xjsp.session.ConnectionContext.java

Source

package com.conwet.xjsp.session;

/*
 * #%L
 * eXtensible JSON Streaming Protocol
 * %%
 * Copyright (C) 2011 - 2014 CoNWeT Lab., Universidad Politcnica de Madrid
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * #L%
 */

import com.conwet.xjsp.features.MessageChannel;
import com.conwet.xjsp.features.SessionListener;
import com.conwet.xjsp.textstream.ConnectionHandler;
import com.conwet.xjsp.errors.ConnectionException;
import java.io.IOException;
import java.nio.channels.WritableByteChannel;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import org.json.simple.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Maintains an incoming buffer for a given connection. The buffer is offered to
 * the active connection state whenever new data arrive or the connection
 * timeout is triggered.
 *
 * The initial state is provided in the constructor.
 *
 * @author sortega
 * @apiviz.owns com.conwet.silbops.xjsp.session.ConnectionState
 * @apiviz.stereotype StatePattern
 */
public class ConnectionContext implements ConnectionHandler {

    private static final Logger logger = LoggerFactory.getLogger(ConnectionContext.class);

    private final ConnectionState initialState;
    private ConnectionState state;
    private MessageChannel channel;
    private final StringBuilder buffer;
    private final List<SessionListener> sessionListeners;
    private AtomicBoolean isOpen;

    public ConnectionContext(ConnectionState initialState) {
        this(initialState, new LinkedList<SessionListener>());
    }

    public ConnectionContext(ConnectionState initialState, List<SessionListener> sessionListeners) {
        this.initialState = initialState;
        this.sessionListeners = sessionListeners;
        this.state = null;
        this.buffer = new StringBuilder();
        this.isOpen = new AtomicBoolean(Boolean.TRUE);
    }

    @Override
    public void start() {
        this.setState(this.initialState);
    }

    /**
     * Transit to a new state which will be activated by calling its start method.
     *
     * @param state the {@linkplain ConnectionState} to use
     */
    public void setState(ConnectionState state) {
        this.state = state;
        try {
            this.state.start(this);
        } catch (ConnectionException ex) {
            handleConnectionException(ex);
        }
    }

    @Override
    public void newData(String data) {
        this.buffer.append(data);
        try {
            state.newData(this);
        } catch (ConnectionException ex) {
            handleConnectionException(ex);
        }
    }

    @Override
    public void setChannel(WritableByteChannel channel) {
        this.channel = new MessageChannel(channel);
    }

    public StringBuilder getBuffer() {
        return this.buffer;
    }

    public MessageChannel getChannel() {
        return this.channel;
    }

    public List<SessionListener> getSessionListeners() {
        return this.sessionListeners;
    }

    @Override
    public void dispose() {

        if (isOpen.getAndSet(Boolean.FALSE)) {

            channel.close();
            state.abort(this);
        }
    }

    @Override
    public void timeout() {
        try {
            this.state.timeout(this);
        } catch (ConnectionException ex) {
            handleConnectionException(ex);
        }
    }

    @Override
    public long getTimeoutPeriod() {
        return this.state.getTimeoutPeriod(this);
    }

    @SuppressWarnings("unchecked")
    private void handleConnectionException(ConnectionException ex) {
        switch (ex.getError()) {
        case ProtocolSyntaxError:
        case NegotiationError:
        case NegotiationFieldError:
        case NegotiationTimeout:
        case ApplicationError:
            logger.error("Closing connection on error: {}", ex.getError());
            this.dispose();
            break;

        default:
            JSONObject payload = new JSONObject();
            payload.put("code", ex.getErrorCode());
            payload.put("message", ex.getMessage());

            if (ex.getMessageId() != null) {
                payload.put("id", ex.getMessageId());
            }

            try {
                this.channel.sendErrorMessage(payload);
            } catch (IOException ex1) {
                logger.error("Unexpected exception", ex1);
            }
        }
    }
}