/*
* Copyright 2015 Julien Viet
*
* 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.
*/
package io.termd.core.http.websocket.server;
import io.termd.core.http.HttpTtyConnection;
import io.undertow.websockets.core.AbstractReceiveListener;
import io.undertow.websockets.core.BufferedBinaryMessage;
import io.undertow.websockets.core.WebSocketChannel;
import io.undertow.websockets.core.WebSockets;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xnio.ChannelListener;
import org.xnio.Pooled;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
/**
* @author <a href="mailto:matejonnet@gmail.com">Matej Lazar</a>
*/
public class WebSocketTtyConnection extends HttpTtyConnection {
private static Logger log = LoggerFactory.getLogger(WebSocketTtyConnection.class);
private WebSocketChannel webSocketChannel;
private final ScheduledExecutorService executor;
private Set<WebSocketChannel> readonlyChannels = new HashSet<>();
@Override
protected void write(byte[] buffer) {
if (isOpen()) {
sendBinary(buffer, webSocketChannel);
}
readonlyChannels.forEach((wsChannel) -> sendBinary(buffer, wsChannel));
}
private void sendBinary(byte[] buffer, WebSocketChannel webSocketChannel) {
WebSockets.sendBinary(ByteBuffer.wrap(buffer), webSocketChannel, null);
}
@Override
public void execute(Runnable task) {
executor.execute(task);
}
@Override
public void schedule(Runnable task, long delay, TimeUnit unit) {
executor.schedule(task, delay, unit);
}
public WebSocketTtyConnection(WebSocketChannel webSocketChannel, ScheduledExecutorService executor) {
this.webSocketChannel = webSocketChannel;
this.executor = executor;
registerWebSocketChannelListener(webSocketChannel);
webSocketChannel.resumeReceives();
}
private void registerWebSocketChannelListener(WebSocketChannel webSocketChannel) {
ChannelListener<WebSocketChannel> listener = new AbstractReceiveListener() {
@Override
protected void onFullBinaryMessage(WebSocketChannel channel, BufferedBinaryMessage message) throws IOException {
log.trace("Server received full binary message");
Pooled<ByteBuffer[]> pulledData = message.getData();
try {
ByteBuffer[] resource = pulledData.getResource();
ByteBuffer byteBuffer = WebSockets.mergeBuffers(resource);
String msg = new String(byteBuffer.array());
log.trace("Sending message to decoder: {}", msg);
writeToDecoder(msg);
} finally {
pulledData.discard();
}
}
};
webSocketChannel.getReceiveSetter().set(listener);
}
public boolean isOpen() {
return webSocketChannel != null && webSocketChannel.isOpen();
}
public void setWebSocketChannel(WebSocketChannel webSocketChannel) {
this.webSocketChannel = webSocketChannel;
}
public void addReadonlyChannel(WebSocketChannel webSocketChannel) {
readonlyChannels.add(webSocketChannel);
}
public void removeReadonlyChannel(WebSocketChannel webSocketChannel) {
readonlyChannels.remove(webSocketChannel);
}
public void removeWebSocketChannel() {
webSocketChannel = null;
}
@Override
public void close() {
Consumer<Void> closeHandler = getCloseHandler();
if (closeHandler != null) {
closeHandler.accept(null);
}
}
}