/** * Copyright 2007-2015, Kaazing Corporation. 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 org.kaazing.k3po.driver.internal.behavior.handler.event; import static java.util.EnumSet.of; import static org.jboss.netty.buffer.ChannelBuffers.copiedBuffer; import static org.jboss.netty.channel.Channels.fireMessageReceived; import static org.jboss.netty.util.CharsetUtil.UTF_8; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import javax.el.ELException; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelEvent; import org.jboss.netty.channel.ChannelFuture; import org.jboss.netty.channel.ChannelHandlerContext; import org.jboss.netty.channel.MessageEvent; import org.jboss.netty.channel.UpstreamMessageEvent; import org.kaazing.k3po.driver.internal.behavior.ScriptProgressException; import org.kaazing.k3po.driver.internal.behavior.handler.codec.Masker; import org.kaazing.k3po.driver.internal.behavior.handler.codec.MessageDecoder; public class ReadHandler extends AbstractEventHandler { private final List<MessageDecoder> decoders; private final Masker unmasker; private final List<MessageDecoder> consumedDecoders; public ReadHandler(List<MessageDecoder> decoders, Masker unmasker) { super(of(ChannelEventKind.MESSAGE)); if (decoders == null) { throw new NullPointerException("decoders"); } else if (decoders.size() == 0) { throw new IllegalArgumentException("must have at least one decoder"); } this.decoders = decoders; this.unmasker = unmasker; this.consumedDecoders = new ArrayList<>(decoders); } @Override public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { messageReceived(ctx, e, false); } @Override protected StringBuilder describe(StringBuilder sb) { sb.append("read "); for (MessageDecoder decoder : decoders) { sb.append(decoder).append(' '); } sb.setLength(sb.length() - 1); return sb; } @Override protected void handleUnexpectedEvent(ChannelHandlerContext ctx, ChannelEvent evt) throws Exception { Channel channel = evt.getChannel(); MessageEvent msg = new UpstreamMessageEvent(channel, copiedBuffer("", UTF_8), channel.getRemoteAddress()); // We create a message with an empty string. We need make sure our decoders get the decoder Last call. messageReceived(ctx, msg, true); // If the above caused a completion we are done. Otherwise we still need to handle the unexpected event if (!getHandlerFuture().isDone()) { super.handleUnexpectedEvent(ctx, evt); } } private void messageReceived(ChannelHandlerContext ctx, MessageEvent e, boolean isLast) throws Exception { ChannelBuffer buf = (ChannelBuffer) e.getMessage(); // first unmask the bytes (if mask read option is specified) buf = unmasker.applyMask(buf); ChannelFuture handlerFuture = getHandlerFuture(); assert handlerFuture != null; Iterator<MessageDecoder> iterator = consumedDecoders.iterator(); while (iterator.hasNext()) { MessageDecoder decoder = iterator.next(); try { if (isLast) { buf = decoder.decodeLast(buf); } else { buf = decoder.decode(buf); } } catch (ELException ele) { ScriptProgressException exception = new ScriptProgressException(getRegionInfo(), ele.getMessage()); exception.initCause(ele); exception.fillInStackTrace(); handlerFuture.setFailure(exception); return; } catch (Exception mme) { // TODO: We will eventually have to create an AstRead node containing what we actually saw. This will come // later. handlerFuture.setFailure(mme); return; } if (buf == null) { // Need more data to complete the decode return; } // Remove the decoder because it is done iterator.remove(); } // If we get through the list of decoders without an exception we are done. handlerFuture.setSuccess(); // Propagate remaining data for next handler(s) if (buf.readable()) { buf = unmasker.undoMask(buf); fireMessageReceived(ctx, buf, ctx.getChannel().getRemoteAddress()); } } }