Package com.starlight.intrepid

Source Code of com.starlight.intrepid.ProxyInvocationHandler

// 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;

import com.starlight.ExceptionKit;
import com.starlight.intrepid.exception.IntrepidRuntimeException;
import com.starlight.intrepid.exception.NotConnectedException;
import com.starlight.intrepid.exception.UnknownMethodException;
import gnu.trove.iterator.TObjectIntIterator;
import gnu.trove.map.TObjectIntMap;
import gnu.trove.map.hash.TObjectIntHashMap;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


/**
* Handles reflective method invocations and dispatches the calls as appropriate.
*/
class ProxyInvocationHandler implements InvocationHandler, Externalizable {
  private static final long serialVersionUID = 0L;

  private static final ThreadLocal<MethodIDTemplate> method_template_local =
    new ThreadLocal<MethodIDTemplate>();

  private static final Method EQUALS_METHOD;
  private static final Method HASHCODE_METHOD;
  private static final Method GET_LOCAL_DELEGATE_METHOD;
  private static final Method GET_HOST_VMID_METHOD;
  private static final Method GET_OBJECT_ID_METHOD;
  private static final Method SET_PERSISTENT_NAME_METHOD;
  private static final Method GET_PERSISTENT_NAME_METHOD;
  static {
    try {
      EQUALS_METHOD = Object.class.getMethod( "equals", Object.class );
      HASHCODE_METHOD = Object.class.getMethod( "hashCode" );

      GET_LOCAL_DELEGATE_METHOD =
        Proxy.class.getMethod( "__intrepid__getLocalDelegate" );
      GET_HOST_VMID_METHOD = Proxy.class.getMethod( "__intrepid__getHostVMID" );
      GET_OBJECT_ID_METHOD = Proxy.class.getMethod( "__intrepid__getObjectID" );
      SET_PERSISTENT_NAME_METHOD =
        Proxy.class.getMethod( "__intrepid__setPersistentName", String.class );
      GET_PERSISTENT_NAME_METHOD =
        Proxy.class.getMethod( "__intrepid__getPersistentName" );
    }
    catch( Exception ex ) {
      throw new IllegalArgumentException(
        "Method not found in static initializer", ex );
    }
  }

  // Set by the SPI when we're deserializing packets so we can tell which instance the
  // proxy is being deserialized into. This helps with finding the appropriate instance
  // to make a call from when there are multiple active instances in a single VM.
  static final ThreadLocal<VMID> DESERIALIZING_VMID = new ThreadLocal<VMID>();


  private VMID vmid;
  private int object_id;

  private TObjectIntMap<MethodIDTemplate> method_to_id_map;
  private final Lock method_map_lock = new ReentrantLock();

  private String persistent_name;

  private volatile Object delegate;

  // VMID of the instance into which this was deserialized
  private transient VMID associated_vmid = null;


  /** FOR EXTERNALIZATION ONLY!!! */
  public ProxyInvocationHandler() {}

  ProxyInvocationHandler( VMID vmid, int object_id,
    TObjectIntMap<MethodIDTemplate> method_to_id_map, String persistent_name,
    Object delegate ) {

    this( vmid, object_id, method_to_id_map, persistent_name, delegate, null );
  }

  ProxyInvocationHandler( VMID vmid, int object_id,
    TObjectIntMap<MethodIDTemplate> method_to_id_map, String persistent_name,
    Object delegate, VMID associated_vmid ) {

//    System.out.println( "Local PIH (" + persistent_name + ") created: " + delegate );

    this.vmid = vmid;
    this.object_id = object_id;
    this.method_to_id_map = method_to_id_map;
    this.persistent_name = persistent_name;
    this.delegate = delegate;
    this.associated_vmid = associated_vmid;
  }


  @Override
  public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable {
    // Methods from Object are not passed on to the source object.
    if ( method.getDeclaringClass().equals( Object.class ) ) {
      if ( method.equals( HASHCODE_METHOD ) ) {
        return Integer.valueOf( hashCode() );
      }
      else if ( method.equals( EQUALS_METHOD ) ) {
        return Boolean.valueOf( proxyEquals( args[ 0 ] ) );
      }

      return method.invoke( this, args );
    }
    // Handle Proxy methods
    else if ( method.getDeclaringClass().equals( Proxy.class ) ) {
      if ( method.equals( GET_OBJECT_ID_METHOD ) ) {
        return Integer.valueOf( object_id );
      }
      else if ( method.equals( GET_HOST_VMID_METHOD ) ) {
        return vmid;
      }
      else if ( method.equals( GET_LOCAL_DELEGATE_METHOD ) ) {
        if ( delegate != null ) return delegate;

        Intrepid local_instance = Intrepid.findLocalInstance( vmid, true );
        if ( local_instance == null ) return null;

        return local_instance.getLocalCallHandler().getLocalDelegate( object_id );
      }
      else if ( method.equals( SET_PERSISTENT_NAME_METHOD ) ) {
        persistent_name = ( String ) args[ 0 ];
        return null;
      }
      else if ( method.equals( GET_PERSISTENT_NAME_METHOD ) ) {
        return persistent_name;
      }
    }

    // If this is a proxy pointing to a local object and the hasn't been serialized
    // yet (so we still have a pointer to the delegate), short-circuit.
    if ( delegate != null ) {
      try {
        return method.invoke( delegate, args );
      }
      catch( InvocationTargetException ex ) {
        throw ex.getCause();
      }
    }

    MethodIDTemplate template = method_template_local.get();
    if ( template == null ) {
      template = new MethodIDTemplate();
      method_template_local.set( template );
    }
    template.set( method );

    final int method_id;

    method_map_lock.lock();
    try {
      method_id = method_to_id_map.get( template );
    }
    finally {
      method_map_lock.unlock();
    }

    if ( method_id == 0 ) {
      throw new UnknownMethodException( method_id, method );
    }

    try {
      Intrepid local_instance = Intrepid.findLocalInstance( vmid, true );
      if ( local_instance != null ) {
        return local_instance.getLocalCallHandler().invoke( object_id,
          method_id, args, true, persistent_name );
      }
      else {
                Intrepid instance = null;
        // See if we have an instance associated from deserialization
        if ( associated_vmid != null ) {
          instance = Intrepid.findLocalInstance( associated_vmid, false );
        }
        if ( instance == null ) {
                  instance = Intrepid.findInstanceWithRemoteSession( vmid );
        }
                if ( instance == null ) {
                    throw new NotConnectedException( vmid );
                }

        try {
          return instance.getRemoteCallHandler().invoke( vmid, object_id,
            method_id, persistent_name, args, method );
        }
        catch( NewIDIndicator id ) {
//          System.out.println( "New IDs - VMID: " + id.getNewVMID() + "  OID: " +
//            id.getNewObjectID() );
          if ( id.getNewVMID() != null ) vmid = id.getNewVMID();
          object_id = id.getNewObjectID();

          if ( id.isThrown() ) throw ( Throwable ) id.getResult();
          else return id.getResult();
        }
      }
    }
    catch( Throwable t ) {
      if ( !ExceptionKit.canThrow( method, t.getClass() ) ) {
        throw new IntrepidRuntimeException( t );
      }
      else throw t;
    }
  }


  @Override
  public boolean equals( Object o ) {
    if ( this == o ) return true;
    if ( o == null || getClass() != o.getClass() ) return false;

    ProxyInvocationHandler that = ( ProxyInvocationHandler ) o;

    if ( object_id != that.object_id ) return false;
    if ( vmid != null ? !vmid.equals( that.vmid ) : that.vmid != null ) return false;

    return true;
  }

  @Override
  public int hashCode() {
    int result = vmid != null ? vmid.hashCode() : 0;
    result = 31 * result + object_id;
    return result;
  }

  @Override
  public String toString() {
    StringBuilder builder = new StringBuilder( "Proxy(" );
    if ( persistent_name != null ) {
      builder.append( "\"" ).append( persistent_name ).append( "\"-" );
    }

    builder.append( object_id ).append( "@" ).append( vmid ).append( "):" );


    return builder.toString();
  }


  VMID getVMID() {
    return vmid;
  }

  int getObjectID() {
    return object_id;
  }


  private boolean proxyEquals( Object o ) {
    if ( this == o ) return true;
    if ( o == null || !Intrepid.isProxy( o ) ) return false;

    Proxy that = ( Proxy ) o;

    if ( object_id != that.__intrepid__getObjectID() ) return false;
    if ( vmid != null ? !vmid.equals( that.__intrepid__getHostVMID() ) :
      that.__intrepid__getHostVMID()!= null ) return false;

    return true;
  }


  ///////////////////////////////////////////////
  // Externalizable

  @Override
  public void writeExternal( ObjectOutput out ) throws IOException {
    // VERSION
    out.writeByte( 0 );

    // VMID
    out.writeObject( vmid );

    // OBJECT ID
    out.writeInt( object_id );

    // METHOD MAP
    out.writeShort( method_to_id_map.size() );
    TObjectIntIterator<MethodIDTemplate> iterator = method_to_id_map.iterator();
    while ( iterator.hasNext() ) {
      iterator.advance();
     
      out.writeObject( iterator.key() );
      out.writeInt( iterator.value() );
    }

    // PERSISTENT NAME
    if ( persistent_name != null ) {
      out.writeBoolean( true );
      out.writeUTF( persistent_name );
    }
    else out.writeBoolean( false );
  }

  @Override
  public void readExternal( ObjectInput in )
    throws IOException, ClassNotFoundException {

    // VERSION
    in.readByte();

    // VMID
    vmid = ( VMID ) in.readObject();

    // OBJECT ID
    object_id = in.readInt();

    // METHOD MAP
    int size = in.readShort();
    method_to_id_map = new TObjectIntHashMap<MethodIDTemplate>( size );
    for( int i = 0; i < size; i++ ) {
      MethodIDTemplate method = ( MethodIDTemplate ) in.readObject();
      int id = in.readInt();
      method_to_id_map.put( method, id );
    }

    // PERSISTENT NAME
    if ( in.readBoolean() ) persistent_name = in.readUTF();

//    System.out.println( "Remote PIH (" + persistent_name + ") created: " + vmid +
//      " - " + object_id );

    // See if we can track the associated Intrepid instance
    associated_vmid = DESERIALIZING_VMID.get();

    // Register with the lease manager
    LeaseManager.registerProxy( this, vmid, object_id );
  }
}
TOP

Related Classes of com.starlight.intrepid.ProxyInvocationHandler

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.