Java tutorial
/* ** Copyright (C) 2013 Mellanox Technologies ** ** 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 com.mellanox.jxio.jxioConnection.impl; import java.io.IOException; import java.net.URI; import java.nio.ByteBuffer; import java.util.ArrayList; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.mellanox.jxio.EventName; import com.mellanox.jxio.EventQueueHandler; import com.mellanox.jxio.EventReason; import com.mellanox.jxio.Msg; import com.mellanox.jxio.MsgPool; import com.mellanox.jxio.ServerPortal; import com.mellanox.jxio.ServerSession; import com.mellanox.jxio.WorkerCache.Worker; import com.mellanox.jxio.exceptions.JxioGeneralException; import com.mellanox.jxio.exceptions.JxioSessionClosedException; import com.mellanox.jxio.jxioConnection.JxioConnectionServer; import com.mellanox.jxio.jxioConnection.JxioConnectionServer.Callbacks; public class ServerWorker extends Thread implements BufferSupplier, Worker { private final static Log LOG = LogFactory.getLog(ServerWorker.class.getCanonicalName()); private final ServerPortal sp; private final EventQueueHandler eqh; public final int portalIndex; private final SessionServerCallbacks callbacks; private final JxioConnectionServer.Callbacks appCallbacks; private final String name; private ArrayList<MsgPool> msgPools; private boolean sessionClosed = false; private boolean waitingToClose = false; private Msg msg = null; private int count = 0; private ServerSession session = null; private boolean stop = false; private URI uri = null; private boolean firstMsg = true; private StreamWorker streamWorker; private boolean notifyDisconnect = false; /** * CTOR of a server worker, each worker is connected to only 1 client at a time * * @param index * - used as worker id * @param uri * - uri from client, is used also to pass data between client and server * @param numMsgs * - number of msgs in msgpool * @param appCallbacks * - callbacks that are called when a new session is started */ public ServerWorker(int index, URI uri, JxioConnectionServer.Callbacks appCallbacks) { portalIndex = index; name = "[ServerWorker " + portalIndex + " ]"; eqh = new EventQueueHandler(new EqhCallbacks(JxioConnectionServer.msgPoolnumMsgs, JxioConnectionServer.msgPoolBuffSize, JxioConnectionServer.msgPoolBuffSize)); this.msgPools = new ArrayList<MsgPool>(); MsgPool pool = new MsgPool(JxioConnectionServer.msgPoolnumMsgs, JxioConnectionServer.msgPoolBuffSize, JxioConnectionServer.msgPoolBuffSize); msgPools.add(pool); eqh.bindMsgPool(pool); sp = new ServerPortal(eqh, uri); callbacks = new SessionServerCallbacks(); this.appCallbacks = appCallbacks; LOG.info(this.toString() + " is up and waiting for requests"); } /** * Main loop of worker thread. * waits in eqh until first msgs is recieved */ public void run() { while (!stop) { LOG.info(this.toString() + " waiting for a new connection"); eqh.runEventLoop(1, -1); // to get the forward going if (notifyDisconnect) { close(); } else { streamWorker.callUserCallback(uri); close(); sessionClosed(); } if (notifyDisconnect) { stop = true; } } eqh.stop(); eqh.close(); } private String getStreamType() { String[] data = uri.getQuery().split("stream="); return data[1].split("&")[0]; } /** * This function is called just before the server listener forwards a new connection to the worker * Reset all variables * * @param ss * - the new session that was constructod for this connection * @param sk * - session key to get the uri */ public void prepareSession(ServerSession ss, URI uri) { session = ss; this.uri = uri; firstMsg = true; String type = getStreamType(); if (type.compareTo("input") == 0) { streamWorker = new OSWorker(this, appCallbacks); } else if (type.compareTo("output") == 0) { streamWorker = new ISWorker(this, appCallbacks); } else { throw new UnsupportedOperationException("Stream type is not recognized"); } } public ServerPortal getPortal() { return sp; } public SessionServerCallbacks getSessionCallbacks() { return callbacks; } /** * clears the session and return worker to pool */ private void sessionClosed() { LOG.info(this.toString() + " disconnected from a Session"); sessionClosed = false; waitingToClose = false; msg = null; count = 0; session = null; } public EventQueueHandler getEqh() { return eqh; } /** * Called from MultiBuffInputStream when an empty buffer is needed. * Send the previous msg (that now it's buffer is full - since we requested an empty one) * wait on eqh until a new msg is recieved */ public ByteBuffer getNextBuffer() throws IOException { if (!firstMsg || msg == null) { sendMsg(); do { eqh.runEventLoop(1, -1); if (notifyDisconnect) { close(); } } while (!notifyDisconnect && !sessionClosed && msg == null); if (sessionClosed) { throw new IOException("Session was closed, no buffer avaliable"); } if (notifyDisconnect) { throw new IOException("Server was closed"); } } firstMsg = false; return streamWorker.getMsgBuffer(msg); } public void sendMsg() { if (msg != null) { count++; try { session.sendResponse(msg); } catch (JxioSessionClosedException e) { LOG.debug(this.toString() + " Error sending message: " + e.toString()); session.discardRequest(msg); } catch (JxioGeneralException e) { LOG.error(this.toString() + " Error sending message: " + e.toString()); session.discardRequest(msg); } msg = null; } } /** * Send msg even if it's buffers is not full */ public void flush() { if (streamWorker.canFlush()) { sendMsg(); } else { throw new UnsupportedOperationException("flush is not supported"); } } /** * Close the session and wait until all msgs are returned to the msgpoll */ private synchronized void close() { if (waitingToClose || session == null) return; sendMsg(); // free last msg if needed LOG.info(this.toString() + " closing session processed " + count + " msgs"); waitingToClose = true; session.close(); while (!sessionClosed) { eqh.runEventLoop(-1, -1); } sessionClosed(); } public void disconnect() { notifyDisconnect = true; if (waitingToClose) return; eqh.breakEventLoop(); } /** * Session Callbacks * */ public class SessionServerCallbacks implements ServerSession.Callbacks { public void onRequest(Msg m) { msg = m; if (waitingToClose) { session.discardRequest(m); } } public void onSessionEvent(EventName event, EventReason reason) { LOG.info(ServerWorker.this.toString() + " got event " + event.toString() + ", the reason is " + reason.toString()); if (event == EventName.SESSION_CLOSED) { sessionClosed = true; waitingToClose = true; eqh.breakEventLoop(); } } public boolean onMsgError(Msg msg, EventReason reason) { if (reason == EventReason.MSG_FLUSHED) { LOG.warn(ServerWorker.this.toString() + " onMsgErrorCallback. reason is " + reason); } else { LOG.error(ServerWorker.this.toString() + " onMsgErrorCallback. reason is " + reason); } return true; } } class EqhCallbacks implements EventQueueHandler.Callbacks { private final ServerWorker outer = ServerWorker.this; private final int numMsgs; private final int inMsgSize; private final int outMsgSize; public EqhCallbacks(int msgs, int in, int out) { numMsgs = msgs; inMsgSize = in; outMsgSize = out; } public MsgPool getAdditionalMsgPool(int in, int out) { MsgPool mp = new MsgPool(numMsgs, inMsgSize, outMsgSize); LOG.warn(this.outer.toString() + " " + outer.toString() + ": new MsgPool: " + mp); outer.msgPools.add(mp); return mp; } } @Override public boolean isFree() { return (session == null); } public String toString() { return this.name; } private static class OSWorker implements StreamWorker { private ServerWorker worker; private Callbacks appCallbacks; public OSWorker(ServerWorker worker, Callbacks appCallbacks) { this.worker = worker; this.appCallbacks = appCallbacks; } public ByteBuffer getMsgBuffer(Msg m) { return m.getOut(); } public void callUserCallback(URI uri) { appCallbacks.newSessionOS(uri, new MultiBufOutputStream(worker)); } public boolean canFlush() { return true; } } private static class ISWorker implements StreamWorker { private ServerWorker worker; private Callbacks appCallbacks; public ISWorker(ServerWorker worker, Callbacks appCallbacks) { this.worker = worker; this.appCallbacks = appCallbacks; } public ByteBuffer getMsgBuffer(Msg m) { return m.getIn(); } public void callUserCallback(URI uri) { appCallbacks.newSessionIS(uri, new MultiBuffInputStream(worker)); } public boolean canFlush() { return false; } } private interface StreamWorker { public ByteBuffer getMsgBuffer(Msg m); public boolean canFlush(); public void callUserCallback(URI uri); } }