Package nexj.core.rpc

Source Code of nexj.core.rpc.RPCUtil$Validator

// 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.Reader;
import java.io.StringReader;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.SimpleTimeZone;
import java.util.TimeZone;

import nexj.core.meta.Attribute;
import nexj.core.meta.Event;
import nexj.core.meta.Metaclass;
import nexj.core.meta.Metadata;
import nexj.core.meta.Primitive;
import nexj.core.meta.PrimitivePrivilege;
import nexj.core.meta.Type;
import nexj.core.persistence.OID;
import nexj.core.rpc.text.TextMarshaller;
import nexj.core.runtime.Context;
import nexj.core.runtime.Instance;
import nexj.core.runtime.InstanceList;
import nexj.core.runtime.InvocationContext;
import nexj.core.runtime.SecurityViolationException;
import nexj.core.runtime.UnitOfWork;
import nexj.core.scripting.ConstPair;
import nexj.core.scripting.Pair;
import nexj.core.scripting.Parser;
import nexj.core.scripting.ParserException;
import nexj.core.scripting.Symbol;
import nexj.core.scripting.object.BasicObject;
import nexj.core.util.Binary;
import nexj.core.util.HashHolder;
import nexj.core.util.HashTab;
import nexj.core.util.Logger;
import nexj.core.util.Lookup;
import nexj.core.util.ObjUtil;
import nexj.core.util.PagedArrayList;
import nexj.core.util.PagedBinary;
import nexj.core.util.PropertyIterator;
import nexj.core.util.PropertyMap;
import nexj.core.util.StringId;
import nexj.core.util.StringUtil;
import nexj.core.util.TZ;
import nexj.core.util.Undefined;

/**
* RPC utilities.
*/
public class RPCUtil
{
   /**
    * Let the top level objects in the graph to pass unrestricted, but restrict the associations.
    */
   public final static int TF_TOP = 0;

   /**
    * Let only the readable objects pass.
    */
   public final static int TF_READABLE = 0x01;

   /**
    * Let all the objects pass.
    */
   public final static int TF_ALL = 0x02;

   /**
    * Transfer the entire object state.
    */
   public final static int TF_STATE = 0x04;

   /**
    * Transfer the old values of the deleted objects.
    */
   public final static int TF_DELETED = 0x08;

   /**
    * Mark the transferred objects as cached.
    */
   public final static int TF_CACHE = 0x10;

   /**
    * The state for new objects will be transferred only if they do not have OIDs.
    */
   public final static int TF_IDENTITY = 0x20;
  
   /**
    * Transfer the old values.
    */
   public final static int TF_OLD = 0x40;

   /**
    * Do not load lazy instances.
    */
   public final static int TF_LAZY = 0x80;

   /**
    * Transfer all non-readable instances as empty objects with class name and null event and OID.
    * This does not apply to collections and top-level instances.
    * No effect if TF_ALL is used.
    */
   public final static int TF_HIDDEN = 0x100;

   /**
    * Do not transfer non-serializable values.
    */
   public final static int TF_SERIALIZABLE = 0x200;

   /**
    * Set during processing of a non-collection association.
    */
   protected final static int TF_REF = 0x80000000;

   /**
    * Do not instantiate object state.
    * @deprecated Use InstanceFactory.* constants instead.
    */
   public final static int INST_IDENTITY = InstanceFactory.IDENTITY;

   /**
    * Instantiate the entire object state.
    * @deprecated Use InstanceFactory.* constants instead.
    */
   public final static int INST_STATE = InstanceFactory.STATE;

   /**
    * Instantiate the pre-values.
    * @deprecated Use InstanceFactory.* constants instead.
    */
   public final static int INST_PRE = InstanceFactory.PRE;

   /**
    * Skip cached instances.
    * @deprecated Use InstanceFactory.* constants instead.
    */
   public final static int INST_CACHE = InstanceFactory.CACHE;

   /**
    * Do not audit the state.
    */
   public final static byte AUDIT_NONE = 0;

   /**
    * Audit only the property map values.
    */
   public final static byte AUDIT_MAP = 1;

   /**
    * Audit only the attribute names.
    */
   public final static byte AUDIT_NAMES = 2;

   /**
    * Audit the current attribute values.
    */
   public final static byte AUDIT_VALUES = 3;

   /**
    * Audit the current and the old (if available) attribute values.
    */
   public final static byte AUDIT_CHANGES = 4;

   // associations

   /**
    * Map of class object to validator.
    */
   protected final static Lookup s_validatorMap = new HashTab(32);

   static
   {
      s_validatorMap.put(Integer.class, Boolean.TRUE);
      s_validatorMap.put(Long.class, Boolean.TRUE);
      s_validatorMap.put(Float.class, Boolean.TRUE);
      s_validatorMap.put(Double.class, Boolean.TRUE);
      s_validatorMap.put(BigDecimal.class, Boolean.TRUE);
      s_validatorMap.put(java.sql.Timestamp.class, Boolean.TRUE);
      s_validatorMap.put(Boolean.class, Boolean.TRUE);
      s_validatorMap.put(String.class, Boolean.TRUE);
      s_validatorMap.put(StringId.class, Boolean.TRUE);
      s_validatorMap.put(Character.class, Boolean.TRUE);
      s_validatorMap.put(Binary.class, Boolean.TRUE);
      s_validatorMap.put(PagedBinary.class, Boolean.TRUE);
      s_validatorMap.put(Locale.class, Boolean.TRUE);
      s_validatorMap.put(TimeZone.class, Boolean.TRUE);
      s_validatorMap.put(TZ.UTC.getClass(), Boolean.TRUE);
      s_validatorMap.put(SimpleTimeZone.class, Boolean.TRUE);
      s_validatorMap.put(Symbol.class, Boolean.TRUE);
      s_validatorMap.put(char[].class, Boolean.TRUE);
      s_validatorMap.put(byte[].class, Boolean.TRUE);
      s_validatorMap.put(String[].class, Boolean.TRUE);
      s_validatorMap.put(Arrays.asList(new Object[0]).getClass(), Boolean.TRUE);

      s_validatorMap.put(ArrayList.class, new Validator()
      {
         public void validate(Object obj, Lookup identityMap, Context context)
         {
            List list = (List)obj;

            for (int i = 0, n = list.size(); i < n; ++i)
            {
               RPCUtil.validate(list.get(i), identityMap, context);
            }
         }
      });

      s_validatorMap.put(PagedArrayList.class, s_validatorMap.get(ArrayList.class));

      s_validatorMap.put(TransferObject.class, new Validator()
      {
         public void validate(Object obj, Lookup identityMap, Context context)
         {
            TransferObject tobj = (TransferObject)obj;

            RPCUtil.validate(tobj.getOID(), identityMap, context);

            for (PropertyIterator itr = tobj.getIterator(); itr.hasNext();)
            {
               itr.next();
               RPCUtil.validate(itr.getValue(), identityMap, context);
            }
         }
      });

      s_validatorMap.put(BasicObject.class, new Validator()
      {
         public void validate(Object obj, Lookup identityMap, Context context)
         {
            BasicObject bobj = (BasicObject)obj;
            int nCount = bobj.getClassObject().resolveAttributeCount();

            for (int i = 0; i < nCount; i++)
            {
               RPCUtil.validate(bobj.getValue(i, context.getMachine()), identityMap, context);
            }
         }
      });

      s_validatorMap.put(OID.class, new Validator()
      {
         public void validate(Object obj, Lookup identityMap, Context context)
         {
            OID oid = (OID)obj;

            for (int i = 0, n = oid.getCount(); i < n; ++i)
            {
               RPCUtil.validate(oid.getValue(i), identityMap, context);
            }
         }
      });

      s_validatorMap.put(Pair.class, new Validator()
      {
         public void validate(Object obj, Lookup identityMap, Context context)
         {
            Pair pair = (Pair)obj;

            for (;;)
            {
               RPCUtil.validate(pair.getHead(), identityMap, context);
               obj = pair.getTail();

               if (obj instanceof Pair)
               {
                  pair = (Pair)obj;
               }
               else
               {
                  RPCUtil.validate(obj, identityMap, context);

                  break;
               }
            }
         }
      });

      s_validatorMap.put(ConstPair.class, s_validatorMap.get(Pair.class));

      s_validatorMap.put(Object[].class, new Validator()
      {
         public void validate(Object obj, Lookup identityMap, Context context)
         {
            Object[] array = (Object[])obj;

            for (int i = 0, n = array.length; i < n; ++i)
            {
               RPCUtil.validate(array[i], identityMap, context);
            }
         }
      });

      s_validatorMap.put(Request.class, new Validator()
      {
         public void validate(Object obj, Lookup identityMap, Context context)
         {
            Request request = (Request)obj;

            RPCUtil.validate(request.getCorrelator(), identityMap, context);

            for (int i = 0, n = request.getInvocationCount(); i < n; ++i)
            {
               Request.Invocation invocation = request.getInvocation(i);

               RPCUtil.validate(invocation.getObject(), identityMap, context);
               RPCUtil.validate(invocation.getArguments(), identityMap, context);
               RPCUtil.validate(invocation.getAttributes(), identityMap, context);
            }

            for (int i = 0, n = request.getFilterCount(); i < n; ++i)
            {
               RPCUtil.validate(request.getFilter(i), identityMap, context);
            }
         }
      });
   }

   // constructors

   /**
    * Prevents construction.
    */
   protected RPCUtil()
   {
   }

   // operations

   /**
    * Validates that a request object graph contains only allowed classes.
    * @param obj The object to validate.
    * @param identityMap Map for tracking object identity.
    * @throws RPCException if the graph is invalid.
    */
   public static void validate(Object obj, Lookup identityMap, Context context) throws RPCException
   {
      if (obj != null)
      {
         Object validator = s_validatorMap.get(obj.getClass());

         if (validator == null)
         {
            throw new RPCException("err.rpc.type", new Object[]{obj.getClass().getName()});
         }

         if (validator != Boolean.TRUE && !identityMap.contains(obj))
         {
            identityMap.put(obj, null);
            ((Validator)validator).validate(obj, identityMap, context);
         }
      }
   }

   /**
    * Instantiates an object from a cached transfer object graph.
    * @param obj The first object graph node.
    * @param identityMap Map for tracking object identity.
    * @param context The invocation context.
    * @return The instantiated value.
    */
   public static Object instantiateDirect(Object obj, Lookup identityMap, InvocationContext context)
   {
      return new InstanceFactory(identityMap, null, InstanceFactory.STATE, context).instantiate(obj);
   }

   /**
    * Instantiates a clean object from a cached transfer object graph (does not instantiate any attributes).
    * @param obj The first object graph node.
    * @param identityMap Map for tracking object identity.
    * @param context The invocation context.
    * @return The instantiated value.
    */
   public static Object instantiateClean(Object obj, Lookup identityMap, InvocationContext context)
   {
      return new InstanceFactory(identityMap, null, InstanceFactory.IDENTITY, context).instantiate(obj);
   }

   /**
    * Instantiates a value, for which the type is not known in advance.
    * @param value The argument value.
    * @param identityMap Map for tracking object identity.
    * @param context The invocation context.
    * @param fixupList The fixup list for the second pass, updated by this method.
    *    Null to instantiate from cache.
    * @param nInst Instantiation mode, one of the INST_* constants.
    * @return The instantiated value.
    * @deprecated Use InstanceFactory instead.
    */
   public static Object instantiate1(Object value, Lookup identityMap, InvocationContext context, List fixupList, int nInst)
   {
      return new InstanceFactory(identityMap, fixupList, nInst, context).instantiate(value);
   }
  
   /**
    * First instantiation pass: creates the instances,
    * checks access rights and populates the fixup list.
    * @param tobj The transfer object to instantiate.
    * @param identityMap The object identity map, updated by this method.
    * @param context The invocation context.
    * @param fixupList The fixup list for the second pass, updated by this method.
    *    Null to instantiate from cache.
    * @param nInst Instantiation mode, one of the INST_* constants.
    * @return The instance corresponding to the transfer object.
    * @deprecated Use InstanceFactory instead.
    */
   public static Instance instantiate1(TransferObject tobj, Lookup identityMap,
      InvocationContext context, List fixupList, int nInst)
   {
      return new InstanceFactory(identityMap, fixupList, nInst, context).instantiate(tobj);
   }

   /**
    * Converts the object state into transfer objects.
    * @param obj The object to convert.
    * @param attributes The attribute list to transfer.
    * @param nTF Combination of TF_* flags.
    * @return The converted object.
    */
   public static Object transfer(Object obj, Pair attributes, int nTF)
   {
      return transfer(obj, attributes, null, new HashTab(), nTF);
   }

   /**
    * Converts the object state into transfer objects.
    * @param obj The object to convert.
    * @param attributes The attribute list to transfer.
    * @param identityMap Map for tracking object identity.
    * @param nTF Combination of TF_* flags.
    * @return The converted object.
    */
   public static Object transfer(Object obj, Pair attributes, Lookup identityMap, int nTF)
   {
      return transfer(obj, attributes, null, identityMap, nTF);
   }

   /**
    * Converts instance state into transfer objects.
    * @param obj The object to convert.
    * @param attributes The attribute list to transfer.
    * @param diffMap Map of instances to original transfer objects. If non-null, only the modified values are transferred.
    * @param identityMap Map for tracking object identity.
    * @param nTF Combination of TF_* flags.
    * @return The converted object.
    */
   public static Object transfer(Object obj, Pair attributes, Lookup diffMap, Lookup identityMap, int nTF)
   {
      if (obj instanceof Instance)
      {
         return transfer((Instance)obj, attributes, diffMap, identityMap, nTF);
      }

      if (obj instanceof InstanceList)
      {
         return transfer((InstanceList)obj, attributes, diffMap, identityMap, nTF);
      }

      return obj;
   }

   /**
    * Converts instance state into transfer objects.
    * @see RPCUtil#transfer(Object, Pair, Lookup, Lookup, int)
    */
   protected static TransferObject transfer(Instance instance, Pair attributes, Lookup diffMap, Lookup identityMap, int nTF)
   {
      Metaclass metaclass = (attributes != null || (nTF & TF_LAZY) == 0) ? instance.getMetaclass() : instance.getLazyMetaclass();
      Attribute lockingAttribute = (metaclass.getPersistenceMapping() == null) ? null : metaclass.getPersistenceMapping().getLockingAttribute();
      TransferObject tobj = (TransferObject)identityMap.get(instance);
      TransferObject orig = (diffMap == null) ? null : (TransferObject)diffMap.get(instance);
      boolean bMerge;

      if (tobj == null)
      {
         if ((nTF & (TF_READABLE | TF_ALL)) == TF_READABLE && !instance.isReadable())
         {
            return ((nTF & (TF_HIDDEN | TF_REF)) == (TF_HIDDEN | TF_REF)) ? new TransferObject(metaclass.getName(), 0) : null;
         }

         tobj = new TransferObject(metaclass.getName());
         tobj.setOID(instance.getOID());

         if ((nTF & TF_CACHE) == 0)
         {
            switch (instance.getState())
            {
               case Instance.NEW:
                  tobj.setEventName("create");

                  if ((nTF & TF_IDENTITY) == 0 || instance.getOID() == null)
                  {
                     nTF |= TF_STATE;
                  }

                  break;

               case Instance.DIRTY:
                  tobj.setEventName("update");
                  break;

               case Instance.DELETED:
                  tobj.setEventName("delete");
                  break;
            }
         }

         if (instance.isLazy())
         {
            tobj.setVersion((short)-1);
         }

         identityMap.put(instance, tobj);
         bMerge = false;
      }
      else
      {
         bMerge = true;
      }

      nTF |= TF_READABLE | TF_REF;

      boolean bOld = (nTF & TF_OLD) != 0 || ((nTF & TF_DELETED) != 0 && instance.getState() == Instance.DELETED);

      for (; attributes != null; attributes = attributes.getNext())
      {
         Object head = attributes.getHead();
         String sName;
         Pair pair;

         if (head instanceof Pair)
         {
            Pair field = (Pair)head;
            Symbol sym = (Symbol)field.getHead();

            if (sym == Symbol.ATAT)
            {
               field = field.getNext();

               if (metaclass.getMetadata().getMetaclass(
                  ((Symbol)field.getHead()).getName()).isUpcast(metaclass))
               {
                  transfer(instance, field.getNext(), diffMap, identityMap, nTF);
               }

               continue;
            }

            if (sym == Symbol.COLON)
            {
               sName = ((Symbol)field.getNext().getHead()).getName();
               tobj.setValue(sName, transfer(instance.findAnnotation(sName), null, diffMap, identityMap, nTF));

               continue;
            }

            sName = sym.getName();
            pair = field.getNext();
         }
         else
         {
            sName = ((Symbol)head).getName();

            if (bMerge && tobj.hasValue(sName))
            {
               continue;
            }

            pair = null;
         }

         Attribute attribute = metaclass.getAttribute(sName);
         Object value = (attribute.isStatic()) ? metaclass.getValue(attribute.getOrdinal()) :
            (bOld) ? instance.getOldValue(attribute.getOrdinal()) : instance.getValue(attribute.getOrdinal());

         if (value instanceof Instance)
         {
            tobj.setValue(sName, transfer((Instance)value, pair, diffMap, identityMap, nTF));
         }
         else if (value instanceof InstanceList)
         {
            tobj.setValue(sName, transfer((InstanceList)value, pair, diffMap, identityMap, nTF));
         }
         else if (orig == null || attribute == lockingAttribute ||
            !ObjUtil.equal(value, orig.findValue(sName, Undefined.VALUE)))
         {
            tobj.setValue(sName, value);
         }
      }

      // For non-persisted objects, add all the attributes with read/write access
      if (!bMerge && (nTF & TF_STATE) != 0 &&
         (tobj.getOID() == null || instance.getState() == Instance.NEW))
      {
         if (lockingAttribute != null)
         {
            instance.getValue(lockingAttribute.getOrdinal());
         }

         for (int i = 0, n = metaclass.getInstanceAttributeCount(); i < n; ++i)
         {
            Attribute attribute = metaclass.getInstanceAttribute(i);
            Object value = instance.getValueDirect(attribute.getOrdinal());

            if (!(value instanceof Undefined) && !tobj.hasValue(attribute.getName()) &&
               instance.isReadable(attribute) && instance.isUpdateable(attribute) &&
               (!attribute.isCalculated() || instance.isOverridden(attribute.getOrdinal())))
            {
               if (value instanceof Instance)
               {
                  tobj.setValue(attribute.getName(), transfer((Instance)value, null, diffMap, identityMap, nTF));
               }
               else if (value instanceof InstanceList)
               {
                  tobj.setValue(attribute.getName(), transfer((InstanceList)value, null, diffMap, identityMap, nTF));
               }
               else if ((orig == null ||
                  attribute == lockingAttribute ||
                  !ObjUtil.equal(value, orig.findValue(attribute.getName(), Undefined.VALUE))) &&
                  ((nTF & TF_SERIALIZABLE) == 0 ||
                   attribute.getType() != Primitive.ANY ||
                   TextMarshaller.isSerializable(value)))
               {
                  tobj.setValue(attribute.getName(), value);
               }
            }
         }
      }

      if ((nTF & TF_CACHE) != 0)
      {
         instance.setCached(true);
      }

      return tobj;
   }

   /**
    * Converts instance state into transfer objects.
    * @see RPCUtil#transfer(Object, Pair, Lookup, Lookup, int)
    */
   protected static List transfer(InstanceList ilist, Pair attributes, Lookup diffMap, Lookup identityMap, int nTF)
   {
      nTF &= ~TF_REF;

      List list = new ArrayList(ilist.size());

      for (Iterator itr = ilist.iterator(); itr.hasNext();)
      {
         Object tobj = transfer(itr.next(), attributes, diffMap, identityMap, nTF);

         if (tobj != null)
         {
            list.add(tobj);
         }
      }

      return list;
   }

   /**
    * Converts the object state into transfer objects.
    * The attributes are not considered for the objects with OID, unless present in valueSet.
    * @param obj The object to convert.
    * @param valueSet Set specifying the instances to be passed by value: [Instance].
    * @param identityMap Map for tracking object identity.
    * @param nTF Combination of TF_* constants.
    * @return The converted object.
    */
   public static Object transferState(Object obj, Set valueSet, Lookup identityMap, int nTF)
   {
      if (obj instanceof Instance)
      {
         Instance instance = (Instance)obj;
         Object result = identityMap.get(obj);

         if (result != null)
         {
            return result;
         }

         Metaclass metaclass = instance.getMetaclass();

         if ((nTF & (TF_READABLE | TF_ALL)) == TF_READABLE && !instance.isReadable())
         {
            return ((nTF & (TF_HIDDEN | TF_REF)) == (TF_HIDDEN | TF_REF)) ? new TransferObject(metaclass.getName(), 0) : null;
         }

         TransferObject tobj = new TransferObject(metaclass.getName());

         tobj.setOID(instance.getOID());
         identityMap.put(obj, tobj);

         if (instance.getOID() != null && metaclass.getPersistenceMapping() != null)
         {
            if (valueSet == null || !valueSet.contains(instance))
            {
               return tobj;
            }

            tobj.setEventName("create");
         }

         nTF |= TF_READABLE | TF_REF;

         for (int i = 0, nCount = metaclass.getInstanceAttributeCount(); i < nCount; ++i)
         {
            Attribute attribute = metaclass.getInstanceAttribute(i);
            Type type = attribute.getType();
            Object value;

            if (type.isPrimitive())
            {
               value = instance.getValue(attribute.getOrdinal());

               if ((nTF & TF_SERIALIZABLE) != 0 &&
                  type == Primitive.ANY &&
                  !TextMarshaller.isSerializable(value))
               {
                  continue;
               }
            }
            else
            {
               value = transferState(instance.getValue(attribute.getOrdinal()), valueSet, identityMap, nTF);
            }

            tobj.setValue(attribute.getName(), value);
         }

         if ((nTF & TF_CACHE) != 0)
         {
            instance.setCached(true);
         }

         return tobj;
      }

      if (obj instanceof InstanceList)
      {
         nTF &= ~TF_REF;

         Collection col = (Collection)obj;
         List list = new ArrayList(col.size());

         for (Iterator itr = col.iterator(); itr.hasNext();)
         {
            Object tobj = transferState(itr.next(), valueSet, identityMap, nTF);

            if (tobj != null)
            {
               list.add(tobj);
            }
         }

         return list;
      }

      return obj;
   }

   /**
    * Converts the object state into transfer objects.
    * Only attributes which values have been updated are considered for the objects with OID.
    * @param obj The object to convert.
    * @param identityMap Map for tracking object identity.
    * @param nTF Combination of TF_* flags.
    * @return The converted object.
    */
   public static Object transferUpdates(Object obj, Lookup identityMap, int nTF)
   {
      if (obj instanceof Instance)
      {
         Instance instance = (Instance)obj;
         Object result = identityMap.get(obj);

         if (result != null)
         {
            return result;
         }

         Metaclass metaclass = instance.getMetaclass();

         if ((nTF & (TF_READABLE | TF_ALL)) == TF_READABLE && !instance.isReadable())
         {
            return ((nTF & (TF_HIDDEN | TF_REF)) == (TF_HIDDEN | TF_REF)) ? new TransferObject(metaclass.getName(), 0) : null;
         }

         TransferObject tobj = new TransferObject(metaclass.getName());
         tobj.setOID(instance.getOID());

         switch (instance.getState())
         {
            case Instance.NEW:
               tobj.setEventName("create");
               nTF |= TF_STATE;
               break;

            case Instance.DIRTY:
               tobj.setEventName("update");
               break;

            case Instance.DELETED:
               tobj.setEventName("delete");
               break;
         }

         identityMap.put(obj, tobj);

         nTF |= TF_READABLE | TF_REF;

         for (int i = 0, nCount = metaclass.getInstanceAttributeCount(); i < nCount; ++i)
         {
            Attribute attribute = metaclass.getInstanceAttribute(i);

            if (attribute.isPersistent() && instance.isDirty(attribute.getOrdinal()))
            {
               Type type = attribute.getType();
               Object value;

               if (type.isPrimitive())
               {
                  value = instance.getValue(attribute.getOrdinal());

                  if ((nTF & TF_SERIALIZABLE) != 0 &&
                     type == Primitive.ANY &&
                     !TextMarshaller.isSerializable(value))
                  {
                     continue;
                  }
               }
               else
               {
                  value = transferUpdates(instance.getValue(attribute.getOrdinal()), identityMap, nTF);
               }

               tobj.setValue(attribute.getName(), value);
            }
         }

         // Set the read access attribute value
         Attribute attribute = metaclass.getReadAccessAttribute();

         if (attribute != null)
         {
            tobj.setValue(attribute.getName(), Boolean.valueOf(instance.isReadable()));
         }

         return tobj;
      }

      if (obj instanceof InstanceList)
      {
         nTF &= ~TF_REF;

         Collection col = (Collection)obj;
         List list = new ArrayList(col.size());

         for (Iterator itr = col.iterator(); itr.hasNext();)
         {
            Object tobj = transferUpdates(itr.next(), identityMap, nTF);

            if (tobj != null)
            {
               list.add(tobj);
            }
         }

         return list;
      }

      return obj;
   }

   /**
    * Converts the object state into transfer objects.
    * The attributes are not considered for the objects with OID, unless specified in the attributes.
    * @param obj The object to convert.
    * @param attributes Attribute list specifying the instances to be passed by value.
    * @param identityMap Map for tracking object identity.
    * @param nTF Combination of TF_* flags.
    * @return The converted object.
    */
   public static Object transferByRefAttributes(Object obj, Pair attributes, Lookup identityMap, int nTF)
   {
      Set instanceSet = new HashHolder();
  
      collectInstances(obj, attributes, instanceSet);
  
      return transferState(obj, instanceSet, identityMap, nTF);
   }

   /**
    * Collects in a map the instances according to the attribute list.
    * @param obj The starting object of the graph.
    * @param attributes The attribute list.
    * @param instanceSet The destination set: [Instance].
    */
   public static void collectInstances(Object obj, Pair attributes, Set instanceSet)
   {
      if (obj == null)
      {
         return;
      }

      if (obj instanceof Instance)
      {
         Instance instance = (Instance)obj;

         instanceSet.add(instance);

         for (; attributes != null; attributes = attributes.getNext())
         {
            Object head = attributes.getHead();

            if (head instanceof Pair)
            {
               Pair field = (Pair)head;
               Symbol sym = (Symbol)field.getHead();

               if (sym == Symbol.ATAT)
               {
                  field = field.getNext();

                  if (instance.getMetaclass().getMetadata().getMetaclass(
                     ((Symbol)field.getHead()).getName()).isUpcast(instance.getMetaclass()))
                  {
                     collectInstances(instance, field.getNext(), instanceSet);
                  }
               }
               else
               {
                  collectInstances(instance.getValue(sym.getName()), field.getNext(), instanceSet);
               }
            }
            else
            {
               collectInstances(instance.getValue(((Symbol)head).getName()), null, instanceSet);
            }
         }
      }

      if (obj instanceof InstanceList)
      {
         for (Iterator itr = ((Collection)obj).iterator(); itr.hasNext();)
         {
            collectInstances(itr.next(), attributes, instanceSet);
         }
      }
   }

   /**
    * Parses a string using a specified parser.
    * @param parser The parser to use.
    * @param sText The string to parse. Can be null.
    * @param defaultValue The default value, if sText is empty.
    * @return The parsed value.
    */
   public static Object parse(Parser parser, String sText, Object defaultValue)
   {
      if (StringUtil.isEmpty(sText))
      {
         return defaultValue;
      }
  
      Reader reader = new StringReader(sText);
  
      try
      {
         Object value = parser.parse(reader, null);
  
         if (value == Parser.EOF)
         {
            return defaultValue;
         }
  
         if (parser.parse(reader, null) != Parser.EOF)
         {
            throw new RPCException("err.rpc.expression", new Object[]{sText});
         }
  
         return value;
      }
      catch (ParserException e)
      {
         throw new RPCException("err.rpc.expression", new Object[]{sText}, e);
      }
   }

   /**
    * Audits unconditionally a single instance or a static event.
    * @param instance The instance (or null for static object).
    * @param tobj The property map containing the audited attributes.
    * @param sClass The affected class.
    * @param sEvent The event name to audit.
    * @param nArgCount The event argument count.
    * @param sAuditClass The audit class name.
    * @param nMode The audit mode, one of the AUDIT_* constants.
    * @param context The invocation context.
    * @return The audit log instance.
    */
   public static Instance audit(Instance instance, PropertyMap tobj,
      String sClass, String sEvent, int nArgCount,
      String sAuditClass, byte nMode, InvocationContext context)
   {
      UnitOfWork uowSaved = context.setAuditUnitOfWork();
      boolean bSecureSaved = context.isSecure();

      try
      {
         context.setSecure(false);

         Instance audit = new Instance(context.getMetadata().getMetaclass(sAuditClass), context);

         audit.setNew();
         audit.setValue("object", instance);
         audit.setValue("class", sClass);
         audit.setValue("event", sEvent);
         audit.setValue("argCount", Primitive.createInteger(nArgCount));
         audit.setValue("user", context.getUser());

         if (nMode != AUDIT_NONE && (instance != null || nMode == AUDIT_MAP))
         {
            PropertyMap map = (PropertyMap)audit.getValue("values");

            for (PropertyIterator itr = tobj.getIterator(); itr.hasNext();)
            {
               itr.next();

               if (nMode <= AUDIT_MAP)
               {
                  Object value;

                  if (nMode == AUDIT_NAMES)
                  {
                     value = null;
                  }
                  else
                  {
                     value = itr.getValue();

                     if (!TextMarshaller.isSerializable(value))
                     {
                        value = String.valueOf(value);
                     }
                  }

                  map.setValue(itr.getName(), value);
               }
               else
               {
                  Attribute attr = instance.getMetaclass().findAttribute(itr.getName());

                  if (attr != null && !attr.isStatic() && attr.getVisibility() == Metaclass.PUBLIC)
                  {
                     Object value = instance.getValueDirect(attr.getOrdinal());

                     if (!(value instanceof Undefined))
                     {
                        if (!TextMarshaller.isSerializable(value))
                        {
                           value = String.valueOf(value);
                        }

                        if (nMode == AUDIT_VALUES)
                        {
                           if (value != null)
                           {
                              map.setValue(attr.getName(), value);
                           }
                        }
                        else
                        {
                           Object oldValue = instance.getOldValueDirect(attr.getOrdinal());

                           if (!ObjUtil.equal(value, oldValue))
                           {
                              map.setValue(attr.getName(), value);

                              if (!(oldValue instanceof Undefined))
                              {
                                 map.setValue('-' + attr.getName(), oldValue);
                              }
                           }
                        }
                     }
                  }
               }
            }
         }

         audit.invoke("create");

         return audit;
      }
      finally
      {
         context.setSecure(bSecureSaved);
         context.setUnitOfWork(uowSaved);
      }
   }

   /**
    * Audits a single instance or a static event.
    * @param instance The instance (or null for static object).
    * @param tob The property map containing the audited attributes.
    * @param event The event to audit.
    * @param bForce True to force the audit regardless of the metadata setting.
    * @param context The invocation context.
    * @return The audit log instance, or null if none created.
    */
   public static Instance audit(Instance instance, PropertyMap tobj, Event event, boolean bForce, InvocationContext context)
   {
      if (bForce || event.isAudited())
      {
         Symbol symbol = event.getSymbol();
         int nArgCount = event.getArgumentCount();
         boolean bStatic = event.isStatic();

         if (instance == null ||
            instance.getUnitOfWork() == null ||
            !instance.getUnitOfWork().isTransient() ||
            nArgCount == 6 && symbol == Symbol.READ && bStatic && instance.getOID() != null)
         {
            byte nMode = (bStatic) ? AUDIT_MAP : AUDIT_NONE;

            switch (nArgCount)
            {
               case 0:
                  if (!bStatic)
                  {
                     if (symbol == Symbol.CREATE || symbol == Symbol.DELETE)
                     {
                        nMode = AUDIT_VALUES;
                     }
                     else if (symbol == Symbol.UPDATE)
                     {
                        nMode = AUDIT_CHANGES;
                     }
                  }

                  break;

               case 6:
                  if (bStatic && symbol == Symbol.READ)
                  {
                     nMode = AUDIT_NAMES;
                  }

                  break;
            }

            return audit(instance, tobj, event.getMetaclass().getName(), event.getName(),
               event.getArgumentCount(), Metadata.AUDIT_LOG_CLASS_NAME, nMode, context);
         }
      }

      return null;
   }

   /**
    * Audits a single instance or a static event.
    * @param instance The instance (or null for static object).
    * @param tob The property map containing the audited attributes.
    * @param event The event to audit.
    * @param context The invocation context.
    * @return The audit log instance, or null if none created.
    */
   public static Instance audit(Instance instance, PropertyMap tobj, Event event, InvocationContext context)
   {
      return audit(instance, tobj, event, false, context);
   }

   /**
    * Logs an exception.
    * For system exceptions, a reference exception is created and returned.
    * @param t The exception to handle.
    * @param bSystem True if it is a system exception.
    * @param sType The request type name.
    * @param logger The logger to use.
    */
   public static Throwable handleException(Throwable t, boolean bSystem, String sType, Logger logger)
   {
      if (bSystem)
      {
         ReferenceException e = new ReferenceException();

         logger.error("Error processing the " + sType + " request (reference id " + e.getId() + ")", t);

         return e;
      }

      int nLevel = (ObjUtil.isError(t)) ? Logger.ERROR : Logger.DEBUG;

      if (logger.isLevelEnabled(nLevel))
      {
         logger.log(nLevel, "Error processing the " + sType + " request", t);
      }

      return t;
   }

   /**
    * Determines whether a given exception is a system error,
    * which should not be shown directly to end-users.
    * @param t The exception to test.
    * @return True if the exception is an error.
    */
   public static boolean isSystem(Throwable t)
   {
      if (t instanceof RequestException)
      {
         return false;
      }

      return ObjUtil.isSystem(t);
   }

   /**
    * Checks if the invocation context user has the generic RPC privilege.
    * @param context The invocation context.
    * @throws SecurityViolationException if the user does not have the privilege.
    */
   public static void checkPrivilege(InvocationContext context) throws SecurityViolationException
   {
      PrimitivePrivilege privilege = context.getMetadata().getGenericRPCPrivilege();

      if (privilege != null && !context.getPrivilegeSet().contains(privilege))
      {
         throw new SecurityViolationException("err.rpc.privilege",
            new Object[]{context.getPrincipal().getName(), privilege.getName()});
      }
   }
  
   // inner classes

   /**
    * Interface implemented by RPC graph validators.
    */
   protected static interface Validator
   {
      /**
       * Validates an RPC object.
       * @param obj The object to validate.
       * @param identityMap The identity map for keeping track of visited objects.
       */
      void validate(Object obj, Lookup identityMap, Context context) throws RPCException;
   }
}
TOP

Related Classes of nexj.core.rpc.RPCUtil$Validator

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.