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

greycat.websocket.WSClient Maven / Gradle / Ivy

The newest version!
/**
 * Copyright 2017 The GreyCat Authors.  All rights reserved.
 * 

* 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 greycat.websocket; import greycat.*; import greycat.base.BaseTaskResult; import greycat.chunk.ChunkType; import greycat.chunk.WorldOrderChunk; import greycat.internal.task.CoreProgressReport; import greycat.plugin.TaskExecutor; import greycat.struct.BufferIterator; import greycat.utility.*; import io.undertow.connector.ByteBufferPool; import io.undertow.server.DefaultByteBufferPool; import io.undertow.websockets.client.WebSocketClient; import io.undertow.websockets.core.*; import greycat.chunk.Chunk; import greycat.plugin.Storage; import greycat.struct.Buffer; import org.xnio.*; import java.io.IOException; import java.net.URI; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; public class WSClient implements Storage, TaskExecutor { private final String _url; private WebSocketChannel _channel; private XnioWorker _worker; private Graph _graph; private Map _callbacks; private final List> _listeners = new ArrayList>(); public WSClient(String p_url) { this._url = p_url; this._callbacks = new ConcurrentHashMap(); } @Override public final void get(Buffer keys, Callback callback) { send_rpc_req(WSConstants.REQ_GET, keys, callback); } @Override public final void put(Buffer stream, Callback callback) { send_rpc_req(WSConstants.REQ_PUT, stream, callback); } @Override public final void putSilent(Buffer stream, Callback callback) { send_rpc_req(WSConstants.REQ_PUT, stream, new Callback() { @Override public void on(Boolean result) { callback.on(null); } }); } @Override public final void remove(Buffer keys, Callback callback) { send_rpc_req(WSConstants.REQ_REMOVE, keys, callback); } @Override public final void lock(Callback callback) { send_rpc_req(WSConstants.REQ_LOCK, null, callback); } @Override public final void unlock(Buffer previousLock, Callback callback) { send_rpc_req(WSConstants.REQ_UNLOCK, previousLock, callback); } @Override public final void connect(final Graph p_graph, final Callback callback) { if (_channel != null) { if (callback != null) { callback.on(true);//already connected } } this._graph = p_graph; try { final Xnio xnio = Xnio.getInstance(io.undertow.websockets.client.WebSocketClient.class.getClassLoader()); _worker = xnio.createWorker(OptionMap.builder() .set(Options.WORKER_IO_THREADS, 2) .set(Options.CONNECTION_HIGH_WATER, 1000000) .set(Options.CONNECTION_LOW_WATER, 1000000) .set(Options.WORKER_TASK_CORE_THREADS, 30) .set(Options.WORKER_TASK_MAX_THREADS, 30) .set(Options.TCP_NODELAY, true) .set(Options.CORK, true) .getMap()); ByteBufferPool _buffer = new DefaultByteBufferPool(true, 1024 * 1024); WebSocketClient.ConnectionBuilder builder = io.undertow.websockets.client.WebSocketClient .connectionBuilder(_worker, _buffer, new URI(_url)); /* if(_sslContext != null) { UndertowXnioSsl ssl = new UndertowXnioSsl(Xnio.getInstance(), OptionMap.EMPTY, _sslContext); builder.setSsl(ssl); }*/ IoFuture futureChannel = builder.connect(); futureChannel.await(5, TimeUnit.SECONDS); //Todo change this magic number!!! if (futureChannel.getStatus() != IoFuture.Status.DONE) { System.err.println("Error during connexion with webSocket"); if (callback != null) { callback.on(null); } } _channel = futureChannel.get(); _channel.getReceiveSetter().set(new MessageReceiver()); _channel.resumeReceives(); if (callback != null) { callback.on(true); } } catch (Exception e) { if (callback != null) { callback.on(false); } e.printStackTrace(); } } @Override public final void disconnect(Callback callback) { try { _channel.sendClose(); _channel.close(); _worker.shutdown(); _channel = null; _worker = null; } catch (IOException e) { e.printStackTrace(); } finally { callback.on(true); } } @Override public final void listen(Callback synCallback) { _listeners.add(synCallback); } @Override public final void execute(final Callback callback, final Task task, final TaskContext prepared) { final Buffer buffer = _graph.newBuffer(); task.saveToBuffer(buffer); final int hashPrint; final int hashProgress; if (prepared != null) { buffer.write(Constants.BUFFER_SEP); final Callback printHook = prepared.printHook(); if (printHook != null) { hashPrint = printHook.hashCode(); _callbacks.put(hashPrint, printHook); Base64.encodeIntToBuffer(hashPrint, buffer); } else { hashPrint = -1; } buffer.write(Constants.BUFFER_SEP); final Callback progressHook = prepared.progressHook(); if (progressHook != null) { hashProgress = progressHook.hashCode(); _callbacks.put(hashProgress, progressHook); Base64.encodeIntToBuffer(hashProgress, buffer); } else { hashProgress = -1; } buffer.write(Constants.BUFFER_SEP); prepared.saveToBuffer(buffer); } else { hashPrint = -1; hashProgress = -1; } send_rpc_req(WSConstants.REQ_TASK, buffer, new Callback() { @Override public void on(final Buffer bufferResult) { if (hashPrint != -1) { _callbacks.remove(hashPrint); } if (hashProgress != -1) { _callbacks.remove(hashProgress); } buffer.free(); final BaseTaskResult baseTaskResult = new BaseTaskResult(null, false); final L3GMap>> collector = new L3GMap>>(true); baseTaskResult.load(bufferResult, 0, _graph, collector); _graph.remoteNotify(baseTaskResult.notifications()); baseTaskResult.loadRefs(_graph, collector, new Callback() { @Override public void on(Boolean result) { bufferResult.free(); callback.on(baseTaskResult); } }); } }); } private class MessageReceiver extends AbstractReceiveListener { @Override protected void onFullBinaryMessage(WebSocketChannel channel, BufferedBinaryMessage message) throws IOException { ByteBuffer byteBuffer = WebSockets.mergeBuffers(message.getData().getResource()); process_rpc_resp(byteBuffer.array()); super.onFullBinaryMessage(channel, message); } @Override protected void onFullTextMessage(WebSocketChannel channel, BufferedTextMessage message) throws IOException { process_rpc_resp(message.getData().getBytes()); super.onFullTextMessage(channel, message); } } private void send_rpc_req(final byte operationId, final Buffer payload, final Callback callback) { if (_channel == null) { throw new RuntimeException(WSConstants.DISCONNECTED_ERROR); } Buffer buffer = _graph.newBuffer(); buffer.write(operationId); buffer.write(Constants.BUFFER_SEP); int hash = callback.hashCode(); _callbacks.put(hash, callback); Base64.encodeIntToBuffer(hash, buffer); if (payload != null) { buffer.write(Constants.BUFFER_SEP); buffer.writeAll(payload.data()); } final ByteBuffer wrapped = ByteBuffer.wrap(buffer.data()); buffer.free(); WebSockets.sendBinary(wrapped, _channel, new WebSocketCallback() { @Override public void complete(WebSocketChannel webSocketChannel, Void aVoid) { } @Override public void onError(WebSocketChannel webSocketChannel, Void aVoid, Throwable throwable) { throwable.printStackTrace(); } }); } /* private void process_notify(Buffer buffer) { Map> events = null; if (buffer != null) { byte type = 0; long world = 0; long time = 0; long id = 0; long hash = 0; int step = 0; long cursor = 0; long previous = 0; int end = (int) buffer.length(); while (cursor < end) { byte current = buffer.read(cursor); if (current == Constants.KEY_SEP) { switch (step) { case 0: type = (byte) Base64.decodeToIntWithBounds(buffer, previous, cursor); break; case 1: world = Base64.decodeToLongWithBounds(buffer, previous, cursor); break; case 2: time = Base64.decodeToLongWithBounds(buffer, previous, cursor); break; case 3: id = Base64.decodeToLongWithBounds(buffer, previous, cursor); break; case 4: hash = Base64.decodeToLongWithBounds(buffer, previous, cursor); break; } previous = cursor + 1; if (step == 4) { step = 0; final Chunk ch = _graph.space().getAndMark(type, world, time, id); if (ch != null) { if (!ch.sync(hash)) { if (events != null && events.get(id) != null) { events.get(id).right().add(time); } else { final WorldOrderChunk wo = (WorldOrderChunk) _graph.space().getAndMark(ChunkType.WORLD_ORDER_CHUNK, 0, 0, id); final Listeners l = wo.listeners(); if (l != null) { LArray collector = new LArray(); collector.add(time); events.put(id, new Tuple(l, collector)); } } } _graph.space().unmark(ch.index()); } } else { step++; } } cursor++; } switch (step) { case 0: type = (byte) Base64.decodeToIntWithBounds(buffer, previous, cursor); break; case 1: world = Base64.decodeToLongWithBounds(buffer, previous, cursor); break; case 2: time = Base64.decodeToLongWithBounds(buffer, previous, cursor); break; case 3: id = Base64.decodeToLongWithBounds(buffer, previous, cursor); break; case 4: hash = Base64.decodeToLongWithBounds(buffer, previous, cursor); break; } if (step == 4) { //invalidate final Chunk ch = _graph.space().getAndMark(type, world, time, id); if (ch != null) { if (!ch.sync(hash)) { if (events != null && events.get(id) != null) { events.get(id).right().add(time); } else { final WorldOrderChunk wo = (WorldOrderChunk) _graph.space().getAndMark(ChunkType.WORLD_ORDER_CHUNK, 0, 0, id); final Listeners l = wo.listeners(); if (l != null) { LArray collector = new LArray(); collector.add(time); events.put(id, new Tuple(l, collector)); } } } _graph.space().unmark(ch.index()); } } //dispatch notification if (events != null) { events.values().forEach(tuple -> { tuple.left().dispatch(tuple.right().all()); }); } } } */ private void process_rpc_resp(byte[] payload) { Buffer payloadBuf = _graph.newBuffer(); payloadBuf.writeAll(payload); BufferIterator it = payloadBuf.iterator(); Buffer codeView = it.next(); if (codeView != null && codeView.length() != 0) { final byte firstCode = codeView.read(0); switch (firstCode) { case WSConstants.NOTIFY_UPDATE: //todo duplicate while (it.hasNext()) { _graph.remoteNotify(it.next()); } //todo optimize this if (_listeners.size() > 0) { final Buffer notifyBuffer = _graph.newBuffer(); notifyBuffer.writeAll(payloadBuf.slice(1, payloadBuf.length() - 1)); for (int i = 0; i < _listeners.size(); i++) { _listeners.get(i).on(notifyBuffer); } notifyBuffer.free(); } break; case WSConstants.NOTIFY_PRINT: final Buffer callbackPrintCodeView = it.next(); final Buffer printContentView = it.next(); final int callbackPrintCode = Base64.decodeToIntWithBounds(callbackPrintCodeView, 0, callbackPrintCodeView.length()); final String printContent = Base64.decodeToStringWithBounds(printContentView, 0, printContentView.length()); final Callback printCallback = _callbacks.get(callbackPrintCode); printCallback.on(printContent); break; case WSConstants.NOTIFY_PROGRESS: final Buffer progressCallbackCodeView = it.next(); final Buffer progressCallbackView = it.next(); final int progressCallbackCode = Base64.decodeToIntWithBounds(progressCallbackCodeView, 0, progressCallbackCodeView.length()); final CoreProgressReport report = new CoreProgressReport(); report.loadFromBuffer(progressCallbackView); Callback progressHook = _callbacks.get(progressCallbackCode); progressHook.on(report); break; case WSConstants.RESP_LOCK: case WSConstants.RESP_GET: case WSConstants.RESP_TASK: final Buffer callBackCodeView = it.next(); final int callbackCode = Base64.decodeToIntWithBounds(callBackCodeView, 0, callBackCodeView.length()); final Callback resolvedCallback = _callbacks.get(callbackCode); final Buffer newBuf = _graph.newBuffer();//will be free by the core boolean isFirst = true; while (it.hasNext()) { if (isFirst) { isFirst = false; } else { newBuf.write(Constants.BUFFER_SEP); } newBuf.writeAll(it.next().data()); } _callbacks.remove(callbackCode); resolvedCallback.on(newBuf); break; default: Buffer genericCodeView = it.next(); final int genericCode = Base64.decodeToIntWithBounds(genericCodeView, 0, genericCodeView.length()); Callback genericCallback = _callbacks.get(genericCode); _callbacks.remove(genericCode); genericCallback.on(true); } } payloadBuf.free(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy