package org.juxtapose.streamline.util.net; import static org.juxtapose.streamline.tools.Preconditions.notNull; import java.util.HashSet; import java.util.Map; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelEvent; import org.jboss.netty.channel.ChannelHandlerContext; import org.jboss.netty.channel.ChannelStateEvent; import org.jboss.netty.channel.ExceptionEvent; import org.jboss.netty.channel.MessageEvent; import org.jboss.netty.channel.SimpleChannelUpstreamHandler; import org.juxtapose.streamline.producer.ISTMEntryKey; import org.juxtapose.streamline.producer.executor.Executable; import org.juxtapose.streamline.producer.executor.IExecutor; import org.juxtapose.streamline.protocol.message.PostMarshaller; import org.juxtapose.streamline.protocol.message.PreMarshaller; import org.juxtapose.streamline.protocol.message.StreamDataProtocol.DataKey; import org.juxtapose.streamline.protocol.message.StreamDataProtocol.DataMap; import org.juxtapose.streamline.protocol.message.StreamDataProtocol.Message; import org.juxtapose.streamline.protocol.message.StreamDataProtocol.RequestMessage; import org.juxtapose.streamline.protocol.message.StreamDataProtocol.SubQueryMessage; import org.juxtapose.streamline.protocol.message.StreamDataProtocol.SubscribeMessage; import org.juxtapose.streamline.stm.ISTM; import org.juxtapose.streamline.util.ISTMEntry; import org.juxtapose.streamline.util.ISTMEntryRequestSubscriber; import org.juxtapose.streamline.util.ISTMRequestor; import org.juxtapose.streamline.util.Status; import org.juxtapose.streamline.util.data.DataType; import com.trifork.clj_ds.IPersistentMap; import com.trifork.clj_ds.PersistentHashMap; /** * @author Pontus J�rgne * May 27, 2012 * Copyright (c) Pontus J�rgne. All rights reserved */ public final class ServerConnectorHandler extends SimpleChannelUpstreamHandler implements ISTMEntryRequestSubscriber, ISTMRequestor { final ISTM stm; ReferenceStore refStore = new ServerReferenceStore(); HashSet< ISTMEntryKey> fullUpdateSent = new HashSet<ISTMEntryKey>(); Channel clientChannel; public ServerConnectorHandler( ISTM inSTM ) { stm = inSTM; } /* (non-Javadoc) * @see org.jboss.netty.channel.SimpleChannelUpstreamHandler#handleUpstream(org.jboss.netty.channel.ChannelHandlerContext, org.jboss.netty.channel.ChannelEvent) */ @Override public final void handleUpstream(ChannelHandlerContext ctx, ChannelEvent e) throws Exception { super.handleUpstream(ctx, e); } /* (non-Javadoc) * @see org.jboss.netty.channel.SimpleChannelUpstreamHandler#messageReceived(org.jboss.netty.channel.ChannelHandlerContext, org.jboss.netty.channel.MessageEvent) */ @Override public final void messageReceived(ChannelHandlerContext ctx, MessageEvent e) { Message message = (Message)e.getMessage(); if( message.getType() == Message.Type.SubQueryMessage ) { SubQueryMessage subMess = message.getSubQueryMessage(); String service = subMess.getService(); Map<String, String> queryMap = PostMarshaller.parseQueryMap( subMess ); int tag = subMess.getTag(); postSubQuery( service, (long)tag, queryMap ); } else if( message.getType() == Message.Type.SubscribeMessage ) { SubscribeMessage subMess = message.getSubscribeMessage(); Integer ref = subMess.getReference(); ISTMEntryKey key; if( subMess.hasKey() ) { //Optimistic subscribe DataKey dKey = subMess.getKey(); key = PostMarshaller.parseKey( dKey ); refStore.addReference( ref, key ); } else { key = refStore.getKeyFromRef( ref ); notNull( key, "There is no key for reference "+ref ); } stm.subscribeToData( key, this ); } else if( message.getType() == Message.Type.RequestMessage ) { RequestMessage rm = message.getRequestMessage(); String service = rm.getService(); int tag = rm.getTag(); long type = rm.getType(); String variable = rm.getVariable(); IPersistentMap<String, DataType<?>> data = PersistentHashMap.EMPTY; if( rm.hasData() ) { DataMap dataMap = rm.getData(); data = PostMarshaller.parseDataMap( dataMap, data ); } stm.request( service, tag, type, this, variable, data ); } else { stm.logError( "Unknown message recieved: "+e.getMessage().getClass() ); } } /** * @param inService * @param inTag * @param inQuery */ public final void postSubQuery( final String inService, final long inTag, final Map<String, String> inQuery ) { stm.execute( new Executable() { @Override public void run() { stm.getDataKey( inService, ServerConnectorHandler.this, inTag, inQuery ); } }, IExecutor.LOW ); } /* (non-Javadoc) * @see org.jboss.netty.channel.SimpleChannelUpstreamHandler#channelConnected(org.jboss.netty.channel.ChannelHandlerContext, org.jboss.netty.channel.ChannelStateEvent) */ @Override public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { clientChannel = ctx.getChannel(); stm.logInfo( "Client connected.." ); } /* (non-Javadoc) * @see org.jboss.netty.channel.SimpleChannelUpstreamHandler#channelDisconnected(org.jboss.netty.channel.ChannelHandlerContext, org.jboss.netty.channel.ChannelStateEvent) */ @Override public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { stm.logInfo( "Client disconnected.." ); removeSubscriptions(); } /* (non-Javadoc) * @see org.jboss.netty.channel.SimpleChannelUpstreamHandler#exceptionCaught(org.jboss.netty.channel.ChannelHandlerContext, org.jboss.netty.channel.ExceptionEvent) */ @Override public void exceptionCaught( ChannelHandlerContext ctx, ExceptionEvent e) { stm.logError( e.toString(), e.getCause() ); e.getChannel().close(); } /** * */ private void removeSubscriptions() { for( ISTMEntryKey key : refStore.getAllKeys() ) { stm.unsubscribeToData( key, this ); } refStore.clear(); } /* (non-Javadoc) * @see org.juxtapose.streamline.util.ISTMEntrySubscriber#updateData(org.juxtapose.streamline.producer.ISTMEntryKey, org.juxtapose.streamline.util.ISTMEntry, boolean) */ @Override public void updateData( ISTMEntryKey inKey, ISTMEntry inData, boolean inFullUpdate ) { //We dont care about initializing update since the client is already in state initializing if( inData.getStatus() == Status.ON_REQUEST ) return; Integer ref = refStore.getRefFromKey( inKey ); notNull( ref, "Reference for key : "+inKey+" not found" ); boolean fullUpdate = fullUpdateSent.remove( inKey ) || inFullUpdate; Message mess = PreMarshaller.createUpdateMessage( ref, inData, fullUpdate ); clientChannel.write( mess ); } /* (non-Javadoc) * @see org.juxtapose.streamline.util.ISTMEntryRequestSubscriber#deliverKey(org.juxtapose.streamline.producer.ISTMEntryKey, java.lang.Object) */ @Override public void deliverKey( ISTMEntryKey inDataKey, Object inTag ) { int ref = refStore.addReference( inDataKey ); fullUpdateSent.add( inDataKey ); Message mess = PreMarshaller.createSubResponse( (Long)inTag, ref,Status.OK, inDataKey, null ); clientChannel.write( mess ); } /* (non-Javadoc) * @see org.juxtapose.streamline.util.ISTMEntryRequestSubscriber#queryNotAvailible(java.lang.Object) */ @Override public void queryNotAvailible( Object inTag ) { Message mess = PreMarshaller.createSubResponse( (Long)inTag, -1, Status.NA ); clientChannel.write( mess ); } /* (non-Javadoc) * @see org.juxtapose.streamline.util.ISTMEntrySubscriber#getPriority() */ @Override public int getPriority() { return IExecutor.LOW; } @Override public void reply( int inTag, long inType, String inMessage, IPersistentMap<String, DataType<?>> inData ) { // TODO Auto-generated method stub } }