/** * @return <tt>true</tt> if the previous ProtocolFilter postExecute method needs to be invoked. */ public boolean postExecute(Context ctx) throws IOException { AIOContext context = (AIOContext) ctx; if (!context.isKeepAlive()) { try { context.getChannel().close(); } catch (AsynchronousCloseException ex) { if (Controller.logger().isLoggable(Level.FINE)) { Controller.logger().log(Level.FINE, "postExecute()", ex); } } finally { context.getController().returnContext(context); } return false; } else { ByteBuffer buffer = ((WorkerThread) Thread.currentThread()).getByteBuffer(); ByteBuffer bb = context.getByteBuffer(); if (bb == null && buffer != null) { bb = buffer; ((WorkerThread) Thread.currentThread()).setByteBuffer(null); } else if (bb == null && buffer == null) { int size = 8192; ByteBufferType bbt = ByteBufferType.DIRECT; if (ctx.getThreadPool() instanceof DefaultThreadPool) { size = ((DefaultThreadPool) ctx.getThreadPool()).getInitialByteBufferSize(); bbt = ((DefaultThreadPool) ctx.getThreadPool()).getByteBufferType(); } bb = ByteBufferFactory.allocate(bbt, size); } bb.clear(); context.setByteBuffer(bb); long timeOut = (Long) ctx.getAttribute("timeout"); context.getChannel().read(bb, timeOut, TimeUnit.SECONDS, null, context); return true; } }
/** * Suspend the request by creating the appropriate structure so the state of the current * connection is not lost. * * @param ctx The current {@link Context} * @param incomingRequest suspend now of after. */ private void suspend(Context ctx, boolean incomingRequest) { try { SelectionKey key = ctx.getSelectionKey(); KeyHandler kh = suspendedKeys.get(key); SuspendableHandlerWrapper<? extends T> sh = null; if (kh != null) { sh = kh.getSuspendableHandler(); } else { kh = new KeyHandler(); } if (kh != null && !incomingRequest) { if (sh.getSuspendWhen() == Suspend.BEFORE) { return; } } // If the users didn't want to be notified. if (sh == null) { // TODO: Configurable. sh = new SuspendableHandlerWrapper( new SuspendableHandler() { public void interupted(Object attachment) {} public void expired(Object attachment) {} public void resumed(Object attachment) {} }, null, 30000, new Suspendable(this), Suspend.AFTER); } sh.setSuspendableFilter(this); sh.suspendable.setKey(key); sh.setSelectorHandler(ctx.getSelectorHandler()); kh.setSuspendableHandler(sh); kh.setKey(key); WorkerThread workerThread = (WorkerThread) Thread.currentThread(); ThreadAttachment attachment = workerThread.getAttachment(); attachment.setMode(Mode.STORE_ALL); kh.setThreadAttachment(workerThread.detach()); ctx.setKeyRegistrationState(KeyRegistrationState.NONE); suspendableMonitor.suspend(kh); } catch (Throwable ex) { if (logger.isLoggable(Level.FINE)) { logger.log(Level.FINE, "suspend", ex); } } }
/** * Excute the pattern matching algorithm to determine if a the current connection must be * suspended or not, and when. * * @param ctx The current {@link Context} * @return true if the ProtocolChain should continue its execution, false if the connection has * been suspended. * @throws java.io.IOException */ public boolean postExecute(Context ctx) throws IOException { if (logger.isLoggable(Level.FINE)) { logger.fine("<----- " + ctx.getKeyRegistrationState()); } if (!suspendedKeys.isEmpty() && ctx.getKeyRegistrationState() == KeyRegistrationState.REGISTER) { suspend(ctx, false); return false; } return true; }
/** * Excute the pattern matching algorithm to determine if a the current connection must be * suspended or not, and when. * * @param ctx The current {@link Context} * @return true if the ProtocolChain should continue its execution, false if the connection has * been suspended. * @throws java.io.IOException */ public boolean execute(Context ctx) throws IOException { WorkerThread wt = (WorkerThread) Thread.currentThread(); ByteBuffer bb = wt.getByteBuffer(); controller = ctx.getController(); if (ctx.getProtocol() == Controller.Protocol.TCP) { ctx.getSelectionKey().attach(null); } else { wt.getAttachment().setTimeout(Long.MIN_VALUE); } if (protocolChain == null) { if (ctx.getProtocolChain() instanceof DefaultProtocolChain) { protocolChain = (DefaultProtocolChain) ctx.getProtocolChain(); } else { throw new IllegalStateException( "SuspendableFilter cannot be " + "used without the DefaultProtocolChain"); } } if (logger.isLoggable(Level.FINE)) { logger.fine("Trying to match " + ctx.getSelectionKey()); } // This will be quite slow if a lot of registration. // TODO: Need a better algorithm. SuspendableHandlerWrapper<? extends T> sh = null; Iterator<byte[]> iterator = suspendCandidates.keySet().iterator(); byte[] matchBytes = null; while (iterator.hasNext()) { matchBytes = iterator.next(); if (Utils.findBytes(bb, matchBytes) > -1) { if (logger.isLoggable(Level.FINE)) { logger.fine( "Find match: " + (new String(matchBytes)) + " Suspending: " + ctx.getSelectionKey()); } sh = suspendCandidates.get(matchBytes); break; } } if (sh != null) { KeyHandler kh = new KeyHandler(); kh.setSuspendableHandler(sh); suspendedKeys.put(ctx.getSelectionKey(), kh); if (sh.getSuspendWhen() == Suspend.BEFORE) { suspend(ctx, true); if (logger.isLoggable(Level.FINE)) { logger.fine("-----> " + ctx.getKeyRegistrationState()); } return false; } } return true; }
/** * Return a {@link Context} to its pool if it is not shared. * * @param ctx - the {@link Context} */ public void returnContext(Context ctx) { if (ctx instanceof NIOContext && ((NIOContext) ctx).decrementRefCount() > 0) { return; } if (logger.isLoggable(Level.FINE)) { logger.log(Level.FINE, "returnContext() Context : " + ctx); } ctx.recycle(); contexts.offer(ctx); }
/** * Configure the {@link Context} * * @param context * @param selectorHandler */ /* package */ public void configureContext(Context context, SelectorHandler selectorHandler) { if (!(context instanceof NIOContext)) { throw new RuntimeException("Invalid Context instance: " + context.getClass().getName()); } NIOContext ctx = (NIOContext) context; ctx.setSelectorHandler(selectorHandler); ctx.setThreadPool(selectorHandler.getThreadPool()); ctx.setAsyncQueueReader(selectorHandler.getAsyncQueueReader()); ctx.setAsyncQueueWriter(selectorHandler.getAsyncQueueWriter()); }
/** * Get an instance of a {@link NIOContext} * * @param key {@link SelectionKey} * @param opType the current SelectionKey op. * @return {@link Context} */ /* package */ public NIOContext pollContext(SelectionKey key, OpType opType) { Context context = contexts.poll(); if (!(context instanceof NIOContext)) { throw new RuntimeException("Invalid Context instance: " + context.getClass().getName()); } NIOContext ctx = (NIOContext) context; ctx.setController(this); ctx.setSelectionKey(key); if (opType != null) { ctx.setCurrentOpType(opType); } else { if (key != null) { ctx.configureOpType(key); } } if (logger.isLoggable(Level.FINE)) { logger.log(Level.FINE, "pollContext(..) Context : " + ctx); } return ctx; }
/** * Resume the connection by register back the SelectionKey for OP event. * * @param key * @return true if the connection was resumed. */ protected boolean resume(SelectionKey key) { KeyHandler kh = suspendedKeys.remove(key); if (kh.getSuspendableHandler() == null) { return false; } if (logger.isLoggable(Level.FINE)) { logger.fine("Resuming: " + kh.getSuspendableHandler()); } kh.getSuspendableHandler() .getSuspendableHandler() .resumed(kh.getSuspendableHandler().getAttachment()); if (kh.getSuspendableHandler().getSuspendWhen() == Suspend.AFTER) { kh.getSuspendableHandler().getSelectorHandler().register(key.channel(), SelectionKey.OP_READ); } else { Context ctx = controller.pollContext(key); controller.configureContext(ctx, kh.getSuspendableHandler().getSelectorHandler()); ctx.execute( SuspendableContextTask.poll(protocolChain, kh.getThreadAttachment(), nextFilterPosition)); } return true; }