Package com.starlight.intrepid.spi.mina

Source Code of com.starlight.intrepid.spi.mina.IoSessionInfoWrapper

// Copyright (c) 2010 Rob Eden.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of Intrepid nor the
//       names of its contributors may be used to endorse or promote products
//       derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package com.starlight.intrepid.spi.mina;

import com.starlight.MiscKit;
import com.starlight.ValidationKit;
import com.starlight.intrepid.ConnectionListener;
import com.starlight.intrepid.VMID;
import com.starlight.intrepid.auth.UserContextInfo;
import com.starlight.intrepid.spi.SessionInfo;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.transport.socket.nio.NioSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.locks.Lock;


/**
* Wraps an {@link IoSession} to implement the {@link SessionInfo} interface.
*/
class IoSessionInfoWrapper implements SessionInfo {
  private static final Logger LOG =
    LoggerFactory.getLogger( IoSessionInfoWrapper.class );

  private static final Method nioSessionGetChannelMethod;

  static {
    Method m = null;
    try {
      // WARNING!!!
      // Use magical reflection powers to get at the underlying SocketChannel. This
      // can fail if a SecurityManager is installed.
      // This is dependent on MINA internals and could break with newer versions!
      m = NioSession.class.getDeclaredMethod( "getChannel" );
      m.setAccessible( true );
    }
    catch( Exception ex ) {
      // ignore
    }
    nioSessionGetChannelMethod = m;
  }


  private final IoSession session;
  private final Map<VMID,SessionContainer> session_map;
  private final Map<HostAndPort,SessionContainer> outbound_session_map;
  private final Map<VMID,VMID> vmid_remap;
  private final Lock map_lock;
  private final ConnectionListener connection_listener;
  private final String connection_type_description;
  private final VMID local_vmid;

  IoSessionInfoWrapper( IoSession session, Map<VMID,SessionContainer> session_map,
    Map<HostAndPort,SessionContainer> outbound_session_map,
    Map<VMID,VMID> vmid_remap, Lock map_lock,
    ConnectionListener connection_listener, String connection_type_description,
    VMID local_vmid ) {

    this.session = session;
    this.session_map = session_map;
    this.outbound_session_map = outbound_session_map;
    this.vmid_remap = vmid_remap;
    this.map_lock = map_lock;
    this.connection_listener = connection_listener;
    this.connection_type_description = connection_type_description;
    this.local_vmid = local_vmid;
  }


  @Override
  public Object getAttribute( Object key ) {
    assert !( key instanceof String ) || !( ( String ) key ).startsWith( "." ) :
      "String keys may not start with '.'";
    return session.getAttribute( key );
  }

  @Override
  public Object setAttribute( Object key, Object value ) {
    LOG.debug( "MINA.SessionInfo setAttribute: {} = {}", key, value );
    assert !( key instanceof String ) || !( ( String ) key ).startsWith( "." ) :
      "String keys may not start with '.'";
    return session.setAttribute( key, value );
  }

  @Override
  public VMID getVMID() {
    return ( VMID ) session.getAttribute( MINAIntrepidSPI.VMID_KEY );
  }



  @Override
  public void setVMID( VMID vmid, byte ack_rate_sec ) {
    // Setting a null VMID is invalid. It will be null by default, but this is okay.
    // Allowing null to be set would allow sessions to disappear from the session_map.
    ValidationKit.checkNonnull( vmid, "VMID" );

    session.setAttribute( MINAIntrepidSPI.INVOKE_ACK_RATE,
      Byte.valueOf( ack_rate_sec ) );

    VMID old_vmid = ( VMID ) session.setAttribute( MINAIntrepidSPI.VMID_KEY, vmid );

    if ( LOG.isDebugEnabled() ) {
      StringBuilder buf = new StringBuilder();
      boolean first = true;
      for( Object key : session.getAttributeKeys() ) {
        if ( first ) first = false;
        else buf.append( ", " );
        buf.append( key );
        buf.append( "=" );
        buf.append( session.getAttribute( key ) );
      }
      LOG.debug( "MINA.SessionInfo setVMID: {} old_vmid: {} attributes: {}",
        vmid, old_vmid, buf.toString() );
    }

    // If the VMID is unchanged, exit
    if ( MiscKit.equal( vmid, old_vmid ) ) {
      // Make sure the VMIDFuture is set
      VMIDFuture future =
        ( VMIDFuture ) session.getAttribute( MINAIntrepidSPI.VMID_FUTURE_KEY );
      assert future != null;
      if ( future != null ) future.setVMID( vmid );

      // Make sure the session is set in the container
      SessionContainer container =
        ( SessionContainer ) session.getAttribute( MINAIntrepidSPI.CONTAINER_KEY );
      if ( container != null ) {
        IoSession old_session = container.setSession( session );
        MINAIntrepidSPI.closeSessionIfDifferent( session, old_session, 2000 );
      }

      return;
    }

    // If the VMID is different from its original value, update the session map.
    map_lock.lock();
    try {
      SessionContainer container = ( SessionContainer )
        session.getAttribute( MINAIntrepidSPI.CONTAINER_KEY );

      if ( old_vmid != null ) {
        SessionContainer old_container = session_map.remove( old_vmid );
        if ( container != null ) {
          outbound_session_map.remove( container.getHostAndPort() );
        }
        vmid_remap.put( old_vmid, vmid );

        // Make sure nothing is pointing to the old key
        if ( vmid_remap.containsValue( old_vmid ) ) {
          Set<VMID> to_remap = new HashSet<VMID>();
          for( Map.Entry<VMID,VMID> entry : vmid_remap.entrySet() ) {
            if ( entry.getValue().equals( old_vmid ) ) {
              to_remap.add( entry.getKey() );
            }
          }
          for( VMID old_old_vmid : to_remap ) {
            vmid_remap.put( old_old_vmid, vmid );
          }
        }

        // Make sure any old connections are cleaned up
        if ( old_container != null && old_container.getSession() != null ) {
          IoSession old_session = old_container.getSession();
          MINAIntrepidSPI.closeSessionIfDifferent( session, old_session, 2000 );
        }

//        System.out.println( "Remap is now: " + vmid_remap );
      }

      if ( container != null ) {
        container.setSession( session );
        session_map.put( vmid, container );

        InetSocketAddress address =
          ( InetSocketAddress ) session.getRemoteAddress();
        connection_listener.connectionOpened( address.getAddress(),
          address.getPort(),
          session.getAttribute( MINAIntrepidSPI.ATTACHMENT_KEY ), local_vmid, vmid,
          getUserContext(), old_vmid,
          connection_type_description, ack_rate_sec );
      }
      else assert false : "Null SessionContainer: " + session;

      // If the connection wasn't initiated by us and the peer has a server port,
      // see if we already have an established connection to it. If so, blow that
      // connection away and use this one.
      Boolean locally_initiated =
        ( Boolean ) session.getAttribute( MINAIntrepidSPI.LOCAL_INITIATE_KEY );
      if ( ( locally_initiated == null || !locally_initiated.booleanValue() ) &&
        getPeerServerPort() != null ) {

        HostAndPort search_template = new HostAndPort(
          ( ( InetSocketAddress ) session.getRemoteAddress() ).getAddress(),
          getPeerServerPort().intValue() );
        SessionContainer current_ob_container =
          outbound_session_map.get( search_template );
        if ( current_ob_container != container ) {
          outbound_session_map.put( search_template, container );

          if ( current_ob_container != null ) {
            IoSession current_session = current_ob_container.getSession();

            // In case there are already listeners on this container, point
            // them to the new session.
            current_ob_container.setSession( session );

            MINAIntrepidSPI.closeSessionIfDifferent( session,
              current_session, 2000 );
          }
        }
      }

    }
    finally {
      map_lock.unlock();
    }

    // NOTE: must come after putting the session in the map to avoid a race condition
    //       where the VMID is returned from the connect method before the connection
    //       is available in the map.
    VMIDFuture future =
      ( VMIDFuture ) session.getAttribute( MINAIntrepidSPI.VMID_FUTURE_KEY );
    assert future != null;
    if ( future != null ) future.setVMID( vmid );
  }

  @Override
  public Byte getProtocolVersion() {
    return ( Byte ) session.getAttribute( MINAIntrepidSPI.PROTOCOL_VERSION_KEY );
  }

  @Override
  public void setProtocolVersion( Byte version ) {
    session.setAttribute( MINAIntrepidSPI.PROTOCOL_VERSION_KEY, version );
    LOG.debug( "MINA.SessionInfo setProtocolVersion: {}", version );
  }


  @Override
  public UserContextInfo getUserContext() {
    return ( UserContextInfo ) session.getAttribute( MINAIntrepidSPI.USER_CONTEXT_KEY );
  }

  @Override
  public void setUserContext( UserContextInfo user_context ) {
    session.setAttribute( MINAIntrepidSPI.USER_CONTEXT_KEY, user_context );
    LOG.debug( "MINA.SessionInfo setUserContext: {}", user_context );
  }


  @Override
  public SocketAddress getRemoteAddress() {
    return session.getRemoteAddress();
  }


  @Override
  public Object getSessionSource() {
    try {
      NioSession nio_session = ( NioSession ) session;

      if ( nioSessionGetChannelMethod != null ) {
        return nioSessionGetChannelMethod.invoke( nio_session );
      }
      else return null;
    }
    catch( Exception ex ) {
      return null;
    }
  }

  @Override
  public Integer getPeerServerPort() {
    return ( Integer ) session.getAttribute( MINAIntrepidSPI.SERVER_PORT_KEY );
  }

  @Override
  public void setPeerServerPort( Integer port ) {
    session.setAttribute( MINAIntrepidSPI.SERVER_PORT_KEY, port );
  }


  @Override
  public Serializable getReconnectToken() {
    return ( Serializable ) session.getAttribute(
      MINAIntrepidSPI.RECONNECT_TOKEN_KEY );
  }

  @Override
  public void setReconnectToken( Serializable reconnect_token ) {
    session.setAttribute( MINAIntrepidSPI.RECONNECT_TOKEN_KEY, reconnect_token );
  }


  @Override
  public ScheduledFuture<?> getReconnectTokenRegenerationTimer() {
    return ( ScheduledFuture<?> ) session.getAttribute(
      MINAIntrepidSPI.RECONNECT_TOKEN_REGENERATION_TIMER );
  }

  @Override
  public void setReconnectTokenRegenerationTimer( ScheduledFuture<?> timer ) {
    session.setAttribute( MINAIntrepidSPI.RECONNECT_TOKEN_REGENERATION_TIMER, timer );
  }



  @Override
  public Byte getAckRateSec() {
    return ( Byte ) session.getAttribute( MINAIntrepidSPI.INVOKE_ACK_RATE );
  }



  @Override
  public String toString() {
    final StringBuilder sb =
      new StringBuilder( "IoSessionInfoWrapper{" );
    sb.append( ", connection_type_description='" )
      .append( connection_type_description )
      .append( '\'' );
    sb.append( ", local_vmid=" ).append( local_vmid );
    sb.append( ", outbound_session_map=" ).append( outbound_session_map );
    sb.append( ", session=" ).append( session );
    sb.append( ", session_map=" ).append( session_map );
    sb.append( ", vmid_remap=" ).append( vmid_remap );
    sb.append( '}' );
    return sb.toString();
  }
}
TOP

Related Classes of com.starlight.intrepid.spi.mina.IoSessionInfoWrapper

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.