Package nexj.core.rpc

Source Code of nexj.core.rpc.TransferObject

// Copyright 2010 NexJ Systems Inc. This software is licensed under the terms of the Eclipse Public License 1.0
package nexj.core.rpc;

import java.io.Externalizable;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.Collection;

import nexj.core.meta.Primitive;
import nexj.core.meta.TypeMismatchException;
import nexj.core.persistence.OID;
import nexj.core.persistence.OIDHolder;
import nexj.core.scripting.Function;
import nexj.core.scripting.Machine;
import nexj.core.scripting.ScriptingException;
import nexj.core.scripting.Symbol;
import nexj.core.util.LookupException;
import nexj.core.util.MathUtil;
import nexj.core.util.PrintWriter;
import nexj.core.util.Printable;
import nexj.core.util.PropertyIterator;
import nexj.core.util.PropertyMap;

/**
* Serializable data transfer object used to pass object state across
* process boundaries.
* The Transfer Objects must contain Object Identifiers (OID). The OIDs are
* used to match the persisted instance to its remote copies. The whole
* Transfer Object graph is serialized along with the request. Individual
* protocols, e.g. SOAP might use internal protocol-specific tables to
* describe the instance references within the serialized graph.
*/
public class TransferObject implements PropertyMap, OIDHolder, Cloneable, Externalizable, Function, Printable
{
   // constants

   /**
    * The Java stream unique identifier for the class.
    */
   private final static long serialVersionUID = 7698169377151199940L;

   /**
    * Serialization version.
    */
   private final static byte SERIAL_VERSION = 0;

   /**
    * Key value indicating deallocated cells.
    */
   private final static Object EMPTY = new Object();

   // attributes

   /**
    * The key and value table.
    * The keys are at even indexes, the values are at odd indexes.
    */
   private Object[] m_table;
  
   /**
    * The key-value pair count.
    */
   private int m_nCount;

   /**
    * The empty element count.
    */
   private int m_nEmpty;

   /**
    * The name of the class corresponding to the transfer object.
    */
   private String m_sClassName;

   /**
    * The name of the event that will be invoked on the instance represented
    * by the Transfer Object. Can be null. Events have no arguments. If a
    * method invocation with arguments is needed, the Command pattern should
    * be used.
    */
   private String m_sEventName;

   /**
    * The release version of the Domain Class represented by this transfer
    * object. The server converts the transfer objects based on this version
    * and on the request version.
    */
   private short m_nVersion;

   // associations

   /**
    * The Transfer Object OID.
    */
   private OID m_oid;

   // constructor
  
   /**
    * Creates a transfer object with a class name and estimated value count.
    * @param sClassName The name of the class stored in the transfer object.
    * @param nValueCount The estimated number of attribute values that will be stored.
    */
   public TransferObject(String sClassName, int nValueCount)
   {
      m_sClassName = sClassName;
      m_table = new Object[getTableSize(nValueCount)];
   }

   /**
    * Creates a transfer object with a class name and estimated value count of 8.
    * @param sClassName The name of the class stored in the transfer object.
    */
   public TransferObject(String sClassName)
   {
      m_sClassName = sClassName;
      m_table = new Object[32];
   }

   /**
    * Creates a transfer object with an estimated value count.
    * @param nValueCount The estimated number of attribute values that will be stored.
    */
   public TransferObject(int nValueCount)
   {
      this(null, nValueCount);
   }

   /**
    * Creates a transfer object with class and event names and an estimated value count.
    * @param sClassName The name of the class stored in the transfer object.
    * @param sEventName The event name.
    * @param nValueCount The estimated number of attribute values that will be stored.
    */
   public TransferObject(String sClassName, String sEventName, int nValueCount)
   {
      this(sClassName, nValueCount);
      m_sEventName = sEventName;
   }

   /**
    * Creates a transfer object with an OID, class and event names and estimated value count.
    * @param oid The OID of the object.
    * @param sClassName The name of the class stored in the transfer object.
    * @param sEventName The event name.
    * @param nValueCount The estimated number of attribute values that will be stored.
    */
   public TransferObject(OID oid, String sClassName, String sEventName, int nValueCount)
   {
      this(sClassName, nValueCount);
      m_oid = oid;
      m_sEventName = sEventName;
   }

   /**
    * Creates a transfer object with class and event names and estimated value count of 8.
    * @param sClassName The name of the class stored in the transfer object.
    * @param sEventName The event name.
    */
   public TransferObject(String sClassName, String sEventName)
   {
      this(sClassName);
      m_sEventName = sEventName;
   }

   /**
    * Creates a transfer object with an estimated value count of 8.
    */
   public TransferObject()
   {
      // Need to explicitly cast to a String in order to allow compiler to
      // properly resolve the constructor when a copy constructor is
      // generated during J2ME translation
      this((String)null);
   }

   // operations
  
   /**
    * Calculates the table size as a power of two.
    * @param nCount The count of key-value pairs to store.
    * @return The calculated size.
    */
   private static int getTableSize(int nCount)
   {
      return MathUtil.ceil2(Math.max(nCount, 1)) << 2;
   }

   /**
    * Sets the transfer object class release version.
    * @param nVersion The transfer object release version to set.
    */
   public void setVersion(short nVersion)
   {
      m_nVersion = nVersion;
   }

   /**
    * @return The transfer object class release version.
    */
   public short getVersion()
   {
      return m_nVersion;
   }

   /**
    * Sets the class name.
    * @param sName The class name to set.
    */
   public void setClassName(String sName)
   {
      m_sClassName = sName;
   }

   /**
    * @return The class name.
    */
   public String getClassName()
   {
      return m_sClassName;
   }

   /**
    * Sets the name of the event that will be invoked on the instance
    * represented by this Transfer Object.
    * @param sName The event name. Can be null.
    */
   public void setEventName(String sName)
   {
      m_sEventName = sName;
   }       

   /**
    * Returns the name of the event that will be invoked on the instance
    * represented by this Transfer Object.
    * @return The event name. Can be null if no event is used.
    */
   public String getEventName()
   {
      return m_sEventName;
   }       

   /**
    * Sets the object identifier.
    * @param oid The object identifier to set.
    */
   public void setOID(OID oid)
   {
      m_oid = oid;
   }

   /**
    * @see nexj.core.persistence.OIDHolder#getOID()
    */
   public OID getOID()
   {
      return m_oid;
   }

   /**
    * Rehashes the transfer object.
    * @param nSize The new table size.
    */
   protected void rehash(int nSize)
   {
      int nMask2 = nSize - 1;
      Object[] table2 = new Object[nSize];
     
      for (int k = 0, nLen = m_table.length; k < nLen; k += 2)
      {
         Object key = m_table[k];
        
         if (key != null && key != EMPTY)
         {
            int i = (key.hashCode() << 1) & nMask2;
           
            for (;;)
            {
               if (table2[i] == null)
               {
                  table2[i] = key;
                  table2[i + 1] = m_table[k + 1];

                  break;
               }
              
               i = (i + 2) & nMask2;
            }
         }
      }
     
      m_table = table2;
      m_nEmpty = 0;
   }
  
   /**
    * Sets an attribute value.
    * @param sName The name of the attribute.
    * @param value The value of the attribute.
    */
   public void setValue(String sName, Object value)
   {
      int nMask = m_table.length - 1;
      int i = (sName.hashCode() << 1) & nMask;
      int k = -1;

      for (;;)
      {
         Object key2 = m_table[i];

         if (key2 == null)
         {
            if (k >= 0)
            {
               i = k;
               --m_nEmpty;
            }
           
            m_table[i] = sName;
            m_table[i + 1] = value;

            if ((++m_nCount << 2) - 1 > nMask)
            {
               rehash((nMask + 1) << 1);
            }

            break;
         }
        
         if (key2 == EMPTY)
         {
            if (k < 0)
            {
               k = i;
            }
         }
         else if (sName.equals(key2))
         {
            m_table[i + 1] = value;

            break;
         }

         i = (i + 2) & nMask;
      }
   }       

   /**
    * Gets an attribute value by name.
    * @param sName The attribute name.
    * @return The attribute value.
    * @throws LookupException if the named value does not exist.
    */
   public Object getValue(String sName) throws LookupException
   {
      int nMask = m_table.length - 1;
      int i = (sName.hashCode() << 1) & nMask;

      for (;;)
      {
         Object key2 = m_table[i];

         if (key2 == null)
         {
            throw new LookupException("err.rpc.valueLookup", new Object[]{sName});
         }

         if (sName.equals(key2))
         {
            return m_table[i + 1];
         }

         i = (i + 2) & nMask;
      }
   }

   /**
    * Finds an attribute value by name.
    * @param sName The attribute name.
    * @param defaultValue The value to return if the attribute was not found.
    * @return The attribute value, or defaultValue if not found.
    */
   public Object findValue(String sName, Object defaultValue)
   {
      int nMask = m_table.length - 1;
      int i = (sName.hashCode() << 1) & nMask;

      for (;;)
      {
         Object key2 = m_table[i];

         if (key2 == null)
         {
            return defaultValue;
         }

         if (sName.equals(key2))
         {
            return m_table[i + 1];
         }

         i = (i + 2) & nMask;
      }
   }

   /**
    * Finds an attribute value by name.
    * @param sName The attribute name.
    * @return The attribute value, or null if not found.
    */
   public Object findValue(String sName)
   {
      return findValue(sName, null);
   }

   /**
    * Checks in the named attribute value exists in the transfer object.
    * @param sName The attribute name.
    * @return True if it exists.
    */
   public boolean hasValue(String sName)
   {
      int nMask = m_table.length - 1;
      int i = (sName.hashCode() << 1) & nMask;

      for (;;)
      {
         Object key2 = m_table[i];

         if (key2 == null)
         {
            return false;
         }

         if (sName.equals(key2))
         {
            return true;
         }

         i = (i + 2) & nMask;
      }
   }
  
   /**
    * Removes an attribute value by name, if it exists.
    * @param sName The attribute name.
    * @return True if the value has been removed, false if not found.
    */
   public boolean removeValue(String sName)
   {
      int nMask = m_table.length - 1;
      int i = (sName.hashCode() << 1) & nMask;

      for (;;)
      {
         Object key2 = m_table[i];

         if (key2 == null)
         {
            return false;
         }

         if (sName.equals(key2))
         {
            --m_nCount;
            ++m_nEmpty;
            m_table[i + 1] = null;

            if (m_table[(i + 2) & nMask] == null)
            {
               do
               {
                  m_table[i] = null;
                  i = (i - 2) & nMask;
                  --m_nEmpty;
               }
               while (m_table[i] == EMPTY);
            }
            else
            {
               m_table[i] = EMPTY;

               if (m_nEmpty >= m_nCount)
               {
                  rehash(getTableSize(m_nCount));
               }
            }

            return true;
         }

         i = (i + 2) & nMask;
      }
   }
  
   /**
    * Removes all the values.
    */
   public void removeAllValues()
   {
      java.util.Arrays.fill(m_table, null);
      m_nCount = m_nEmpty = 0;
   }

   /**
    * @return The stored attribute value count.
    */
   public int getValueCount()
   {
      return m_nCount;
   }

   /**
    * @return PropertyIterator for attribute keys and values.
    */
   public PropertyIterator getIterator()
   {
      return new Iterator();
   }

   /**
    * @see nexj.core.scripting.Function#invoke(int, nexj.core.scripting.Machine)
    */
   public boolean invoke(int nArgCount, Machine machine)
   {
      if (nArgCount != 0)
      {
         Object sym = machine.getArg(0, nArgCount);

         if (sym instanceof Symbol || sym instanceof String)
         {
            String sName = sym.toString();

            if (nArgCount == 1)
            {
               if (sName.length() > 0 && sName.charAt(0) == ':')
               {
                  if (sym == Symbol._EVENT)
                  {
                     machine.returnValue(getEventName(), nArgCount);
                  }
                  else if (sym == Symbol._CLASS)
                  {
                     machine.returnValue(getClassName(), nArgCount);
                  }
                  else if (sym == Symbol._ITERATOR)
                  {
                     machine.returnValue(getIterator(), nArgCount);
                  }
                  else if (sym == Symbol._OID)
                  {
                     machine.returnValue(getOID(), nArgCount);
                  }
                  else if (sym == Symbol._VERSION)
                  {
                     machine.returnValue(Primitive.createInteger(m_nVersion), nArgCount);
                  }
                  else if (sym == Symbol._PRE)
                  {
                     machine.returnValue(findValue(sName), nArgCount);
                  }
                  else
                  {
                     throw new ScriptingException("err.scripting.tobjAttribute", new Object[]{sName});
                  }
               }
               else
               {
                  machine.returnValue(findValue(sName), nArgCount);
               }
            }
            else if (nArgCount == 2)
            {
               Object value = machine.getArg(1, nArgCount);

               if (sName.length() > 0 && sName.charAt(0) == ':')
               {
                  if (sym == Symbol._EVENT)
                  {
                     if (value != null && !(value instanceof String))
                     {
                        throw new TypeMismatchException(Symbol._EVENT);
                     }
                    
                     setEventName((String)value);
                  }
                  else if (sym == Symbol._CLASS)
                  {
                     if (value != null && !(value instanceof String))
                     {
                        throw new TypeMismatchException(Symbol._CLASS);
                     }
                    
                     setClassName((String)value);
                  }
                  else if (sym == Symbol._OID)
                  {
                     if (value != null && !(value instanceof OID))
                     {
                        throw new TypeMismatchException(Symbol._OID);
                     }
                    
                     setOID((OID)value);
                  }
                  else if (sym == Symbol._VERSION)
                  {
                     if (!(value instanceof Number))
                     {
                        throw new TypeMismatchException(Symbol._VERSION);
                     }
                    
                     setVersion(((Number)value).shortValue());
                  }
                  else if (sym == Symbol._CONTAINS)
                  {
                     if (value instanceof Symbol)
                     {
                        value = Boolean.valueOf(hasValue(value.toString()));
                     }
                     else if (value instanceof String)
                     {
                        value = Boolean.valueOf(hasValue((String)value));
                     }
                     else
                     {
                        throw new TypeMismatchException(Symbol._CONTAINS);
                     }
                  }
                  else if (sym == Symbol._GET)
                  {
                     if (value instanceof Symbol)
                     {
                        value = getValue(value.toString());
                     }
                     else if (value instanceof String)
                     {
                        value = getValue((String)value);
                     }
                     else
                     {
                        throw new TypeMismatchException(Symbol._GET);
                     }
                  }
                  else if (sym == Symbol._REMOVE)
                  {
                     if (value instanceof Symbol)
                     {
                        value = Boolean.valueOf(removeValue(value.toString()));
                     }
                     else if (value instanceof String)
                     {
                        value = Boolean.valueOf(removeValue((String)value));
                     }
                     else
                     {
                        throw new TypeMismatchException(Symbol._REMOVE);
                     }
                  }
                  else if (sym == Symbol._PRE)
                  {
                     setValue(sName, value);
                  }
                  else
                  {
                     throw new ScriptingException("err.scripting.tobjAttribute", new Object[]{sName});
                  }
               }
               else
               {
                  setValue(sName, value);
               }

               machine.returnValue(value, nArgCount);
            }
            else
            {
               throw new ScriptingException("err.scripting.maxArgCount",
                  new Object[]{sym.toString(),
                     Primitive.ONE_INTEGER,
                     Primitive.createInteger(nArgCount - 1)});
            }

            return false;
         }
         else
         {
            throw new ScriptingException("err.scripting.funCall");
         }
      }
      else
      {
         throw new ScriptingException("err.scripting.minArgCount",
            new Object[]{"TransferObject",
               Primitive.ONE_INTEGER,
               Primitive.createInteger(nArgCount)});
      }
   }

   /**
    * @see java.lang.Object#clone()
    */
   public Object clone()
   {
      try
      {
         TransferObject tobj = (TransferObject)super.clone();

         tobj.m_table = new Object[m_table.length];
         System.arraycopy(m_table, 0, tobj.m_table, 0, m_table.length);

         return tobj;
      }
      catch (CloneNotSupportedException e)
      {
         return null;
      }
   }

   /**
    * @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
    */
   public void writeExternal(ObjectOutput out) throws IOException
   {
      out.writeByte(SERIAL_VERSION);
      out.writeObject(m_sClassName);
      out.writeObject(m_sEventName);
      out.writeShort(m_nVersion);
      out.writeObject(m_oid);
      out.writeInt(m_nCount);

      int nCount = m_nCount;

      for (int i = 0; nCount != 0; i += 2)
      {
         Object key = m_table[i];
        
         if (key != null && key != EMPTY)
         {
            out.writeObject(key);
            out.writeObject(m_table[i + 1]);
            --nCount;
         }
      }
   }

   /**
    * @see java.io.Externalizable#readExternal(java.io.ObjectInput)
    */
   public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
   {
      // Read the serial version
      in.readByte();
     
      m_sClassName = (String)in.readObject();
      m_sEventName = (String)in.readObject();
      m_nVersion = in.readShort();
      m_oid = (OID)in.readObject();
     
      int nCount = in.readInt();

      if (nCount < 0)
      {
         throw new InvalidObjectException("Negative TransferObject value count");
      }
     
      int nLen = getTableSize(nCount);
     
      if (nLen != m_table.length)
      {
         m_table = new Object[nLen];
      }
     
      while (nCount-- != 0)
      {
         Object key = in.readObject();
         setValue((String)key, in.readObject());
      }
   }

   /**
    * @see nexj.core.util.Printable#printOn(nexj.core.util.PrintWriter)
    */
   public void printOn(PrintWriter writer) throws IOException
   {
      boolean bNew = writer.addObject(this);

      if (!bNew)
      {
         writer.write(PrintWriter.REF);
      }

      writer.write("TO<");

      if (m_sClassName != null)
      {
         writer.write(m_sClassName);
      }

      if (m_sEventName != null || m_nVersion != 0)
      {
         if (m_sEventName != null)
         {
            writer.write('\'');
            writer.write(m_sEventName);
         }

         writer.write(',');

         if (m_nVersion != 0)
         {
            writer.write(' ');
            writer.print(m_nVersion);
         }
      }

      writer.write(", ");

      if (m_oid != null)
      {
         writer.write(m_oid.toString());
      }
      else
      {
         writer.printId(this);
      }

      writer.write('>');

      if (bNew)
      {
         writer.write('(');
         writer.addIndent(1);

         for (PropertyIterator propItr = getIterator(); propItr.hasNext();)
         {
            propItr.next();
            writer.indent();
            writer.write(propItr.getName());
            writer.write('=');

            Object value = propItr.getValue();

            if (value instanceof Collection)
            {
               writer.addIndent(1);
               writer.write('{');

               Collection col = (Collection)value;

               for (java.util.Iterator itemItr = ((Collection)value).iterator(); itemItr.hasNext();)
               {
                  writer.indent();
                  writer.print(itemItr.next());

                  if (itemItr.hasNext())
                  {
                     writer.write(',');
                  }
               }

               writer.addIndent(-1);

               if (!col.isEmpty())
               {
                  writer.indent();
               }

               writer.write('}');
            }
            else
            {
               writer.print(value);
            }

            if (propItr.hasNext())
            {
               writer.write(',');
            }
         }

         writer.addIndent(-1);

         if (getValueCount() != 0)
         {
            writer.indent();
         }

         writer.write(')');
      }
   }

   /**
    * @see java.lang.Object#toString()
    */
   public String toString()
   {
      return PrintWriter.toString(this);
   }

   // inner classes

   /**
    * Transfer Object iterator - can return the current key and the value.
    * NOTE: This iterator does not check for errors for efficiency reasons.
    */
   protected class Iterator implements PropertyIterator
   {
      /**
       * The next key index.
       */
      private int m_nNext = -2;
     
      /**
       * The current key index.
       */
      private int m_nCur;

      /**
       * The table for iteration.
       */
      protected Object[] m_table = TransferObject.this.m_table;

      /**
       * Creates an iterator.
       */
      protected Iterator()
      {
         incr();
      }

      /**
       * Advances to the next item.
       * @return True if there is next item.
       */
      private boolean incr()
      {
         m_nCur = m_nNext;
         m_nNext += 2;
        
         while (m_nNext < m_table.length)
         {
            if (m_table[m_nNext] != null && m_table[m_nNext] != EMPTY)
            {
               return true;
            }
           
            m_nNext += 2;
         }
        
         return false;
      }
     
      /**
       * @see java.util.Iterator#hasNext()
       */
      public boolean hasNext()
      {
         return m_nNext < m_table.length;
      }

      /**
       * @return The next available element key.
       * @see java.util.Iterator#next()
       */
      public Object next()
      {
         if (m_nNext < m_table.length)
         {
            incr();

            return m_table[m_nCur];
         }

         throw new java.util.NoSuchElementException();
      }

      /**
       * @see java.util.Iterator#remove()
       */
      public void remove()
      {
         if (m_table[m_nCur] == null || m_table[m_nCur] == EMPTY)
         {
            return;
         }

         if (m_table == TransferObject.this.m_table)
         {
            int nMask = m_table.length - 1;
           
            --m_nCount;
            ++m_nEmpty;
            m_table[m_nCur + 1] = null;

            if (m_table[(m_nCur + 2) & nMask] == null)
            {
               int i = m_nCur;
              
               do
               {
                  m_table[i] = null;
                  i = (i - 2) & nMask;
                  --m_nEmpty;
               }
               while (m_table[i] == EMPTY);
            }
            else
            {
               m_table[m_nCur] = EMPTY;
              
               if (m_nEmpty >= m_nCount)
               {
                  rehash(getTableSize(m_nCount));
               }
            }
         }
         else
         {
            removeValue((String)m_table[m_nCur]);
            m_table[m_nCur] = m_table[m_nCur + 1] = null;
         }
      }
     
      /**
       * @return The value name retrieved by the last next() invocation.
       */
      public String getName()
      {
         return (String)m_table[m_nCur];
      }
     
      /**
       * @return The value associated with the last next() invocation.
       */
      public Object getValue()
      {
         return m_table[m_nCur + 1];
      }
     
      /**
       * Replaces the value associated with the last next() invocation.
       * @param value The value to set.
       */
      public void setValue(Object value)
      {
         m_table[m_nCur + 1] = value;

         if (m_table != TransferObject.this.m_table)
         {
            TransferObject.this.setValue((String)m_table[m_nCur], value);
         }
      }
   }
}
TOP

Related Classes of nexj.core.rpc.TransferObject

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.