001//
002// Name
003//  $RCSfile: ManagedObject.java,v $
004//
005// Copyright
006//  Copyright 2007-2015 Cloud Software Group, Inc. ALL RIGHTS RESERVED.
007//  Cloud Software Group, Inc. Confidential Information
008//
009// History
010//  $Revision: 1.1.2.47.2.1 $ $Date: 2018/07/23 15:20:27 $
011//
012
013package com.kabira.platform;
014
015import com.kabira.platform.annotation.Managed;
016import java.util.Iterator;
017
018/**
019  * Class to manipulate and find shared memory backed objects.
020  * To create a Java object that persists in shared memory, see the
021  * <code>&#64;Managed</code> annotation.
022  * <p>
023  *  Example:
024  *<pre>&#64;Managed
025public class Account {
026    public Account(String userName) {
027    super();
028    m_userName = userName;
029    }
030
031    public Account(String userName, Account referrer) {
032    this(userName);
033    allocateMailbox();
034    addToFriendList(referrer);
035    }
036
037    public void addToFriendList(Account friendlyAccount) {
038    ...
039    }
040
041    private void allocateMailbox() {
042    ...
043    }
044
045    private String m_userName;
046    private Account[] m_friendList;
047}</pre>
048 *  Some example usage of the above type:
049 <pre>
050    //
051    //  create a new account in shared memory:
052    //
053    Account newCustomer = new Account("Fred Rogers", referrer);
054    System.out.println("Added " + newCustomer.toString());
055
056    //
057    //  delete all expired accounts from shared memory.
058    //  Note extent access, and use of the delete() method
059    //  to delete the shared memory instance.
060    //
061    for (Account acc : ManagedObject.extent(Account.class)) {
062    if (acc.expireDate &lt; today) {
063        ManagedObject.delete(acc);
064    }
065    }
066 </pre>
067 *
068 *  @see com.kabira.platform.annotation.Managed
069 *  @see com.kabira.platform.Transaction
070 */
071@Managed
072public class ManagedObject
073{
074    /**
075     * Returns all shared memory instances of a Managed type.
076     * <p>
077     * The <code>klass</code> parameter must be a <code>Managed</code>
078     * class, and is used to determine the extent to return.
079     * A Managed class is either annotated <code>&#64;Managed</code>, or
080     * extends a class that is so annotated.
081     * <p>
082     * Consider the following example.  It returns all instances of
083     * <code>MyType</code> (including instances of its subtypes)
084     * with no locks held on the objects:
085    <pre>
086    //
087    // Iterate instances of MyType.class, returning all objects in the extent
088    //
089    for (MyType instance : ManagedObject.extent(MyType.class)) {
090    assert ( Transaction.hasReadLock(instance) == false );
091    assert ( Transaction.hasWriteLock(instance) == false );
092    ...
093    }</pre>
094     * @param <T> Managed class.
095     * @param klass extent class - instances of this type,
096     * and any subtypes that extend from this type, will be returned in
097     * the extent.
098     * @exception ManagedClassError Thrown if <code>klass</code>
099     *   is not Managed (or exactly ManagedObject).
100     * @return <code>Iterable</code> that will traverse all instances 
101     *   located in shared memory.
102     * @see com.kabira.platform.annotation.Managed
103     * @see com.kabira.platform.ManagedClassError
104     */
105    public static <T> java.lang.Iterable<T> extent(
106        final java.lang.Class<T> klass)
107        throws ManagedClassError
108    {
109        return ManagedObject.extent(klass, LockMode.NOLOCK);
110    }
111
112    /**
113     * Returns all shared memory instances of a Managed type,
114     * with an explicit transaction lock taken on each instance
115     * as it is iterated.
116     * <p>
117     * The <code>klass</code> parameter must be a Managed class,
118     * and is used to determine the extent to return.
119     * <p>
120     * The <code>objectLock</code> parameter is used to specify the
121     * transaction lock to take on objects returned during extent 
122     * iteration. The objects are locked as they are returned from the 
123     * <code>java.util.Iterator</code>, not when this method
124     * returns.
125     * <p>
126     * Consider the following example.  It returns all instances of
127     * <code>MyType</code> (including instances of its subtypes)
128     * with a READLOCK held on each instance:
129     <pre>
130    //
131    //  Iterate the ManagedObjectSet taking read locks on the objects
132    //
133    for (MyType mt : ManagedObject.extent( MyType.class, LockMode.READLOCK)) {
134    assert ( Transaction.hasReadLock(mt) == true );
135    ...
136    }</pre>
137     * @param <T> Managed class.
138     * @param klass extent class - instances of this type,
139     *   and any subtypes that extend from this type, will be returned
140     *   in the extent.
141     * @param objectLock object lock - all object instances
142     *   returned during extent iteration will have this transaction lock.
143     * @exception ManagedClassError Thrown if <code>klass</code>
144     *   is not marked Managed, or is exactly ManagedObject.
145     * @return <code>Iterable</code> containing references to
146     *   all instances located in shared memory.
147     * @see com.kabira.platform.annotation.Managed
148     */
149    public static <T> java.lang.Iterable<T> extent(
150        final java.lang.Class<T> klass,
151        final LockMode objectLock)
152        throws ManagedClassError
153    {
154        checkManagedClass(klass, "extent");
155
156        if (klass.equals(ManagedObject.class))
157        {
158            throw new ManagedClassError(
159                "extent class cannot be "
160                + ManagedObject.class.getName() + ".");
161        }
162
163        return new Extent<T>(klass.getName(), objectLock);
164    }
165
166    /**
167     * Returns all shared memory instances of a Managed type,
168     * with an explicit transaction lock taken on each instance
169     * as it is iterated.
170     * <p>
171     * The <code>klass</code> parameter must be a Managed class,
172     * and is used to determine the extent to return.
173     * <p>
174     * The <code>queryScope</code> parameter determines the scope
175     * of the search for object instances.
176     * <p>
177     * The <code>objectLock</code> parameter is used to specify the
178     * transaction lock to take on objects returned during extent 
179     * iteration. The objects are locked as they are returned from the 
180     * <code>java.util.Iterator</code>, not when this method
181     * returns.
182     * @param <T> Managed class.
183     * @param klass extent class - instances of this type,
184     *   and any subtypes that extend from this type, will be returned
185     *   in the extent.
186     * @param queryScope scope of extent lookup.
187     * @param objectLock object lock - all object instances
188     *   returned during extent iteration will have this transaction lock.
189     * @exception ManagedClassError Thrown if <code>klass</code>
190     *   is not marked Managed, or is exactly ManagedObject.
191     * @exception IllegalArgumentException
192     *  The queryScope failed audit due to a bad node list.
193     * @exception ResourceUnavailableException
194     *  Access to distributed services failed.
195     * @return <code>Iterable</code> containing references to
196     *   all instances located in shared memory.
197     * @see com.kabira.platform.annotation.Managed
198     * @see com.kabira.platform.QueryScope
199     */
200    public static <T> java.lang.Iterable<T> extent(
201        final java.lang.Class<T> klass,
202        final QueryScope queryScope,
203        final LockMode objectLock)
204        throws ManagedClassError,
205                IllegalArgumentException, 
206                ResourceUnavailableException
207    {
208        checkManagedClass(klass, "extent");
209
210        if (klass.equals(ManagedObject.class))
211        {
212            throw new ManagedClassError(
213                "extent class cannot be "
214                + ManagedObject.class.getName() + ".");
215        }
216
217        if (queryScope.getScope() != QueryScope.Scope.LOCAL)
218        {
219            initializeDistributedQuery();
220            queryScope.audit();
221            if (m_distributedQuery != null)
222            {
223                TypeDescriptor td = TypeDescriptor.getDescriptor(klass);
224                m_distributedQuery.loadObjectExtent(
225                        queryScope,
226                        td.m_domainId,
227                        td.m_typeId,
228                        KeyQuery.mapObjectLock(objectLock));
229            }
230        }
231        return new Extent<T>(klass.getName(), objectLock);
232    }
233
234    /** Returns a hash code based on the underlying shared memory object.
235      * @param o Managed object to hash
236      * @return calculated hash code.
237      */
238    public static int hashCode(Object o)
239        throws ManagedClassError
240    {
241        return java.util.Arrays.hashCode(getObjectReference(o, "hashCode"));
242    }
243
244    /** Returns true if both references are proxies to the same
245      * shared memory object.
246      * @param o1 Object to compare
247      * @param o2 Object to compare
248      * @return true if the two instances refer to the same Managed object.
249      */
250    public static boolean equals(Object o1, Object o2)
251    {
252        if (o1 == o2)
253        {
254            return true;
255        }
256
257        if ((o1 == null) || (o2 == null))
258        {
259            return false;
260        }
261
262        if ((isManaged(o1) == false) || (isManaged(o2) == false))
263        {
264            return false;
265        }
266
267        return java.util.Arrays.equals(
268            getObjectReference(o1, "equals"),
269            getObjectReference(o2, "equals"));
270    }
271
272    /** Determines if the given object is an instance of a Managed class.
273      * Returns true only if the object, or a superclass, is marked
274      * &#64;Managed.
275      * @param o Object to check.
276      * @return true if object is a Managed object.
277      */
278    public static boolean isManaged(Object o)
279    {
280        return o instanceof ManagedMarker;
281    }
282
283    /** Determines if the given class is Managed.
284      * Returns true only if the class, or a superclass, is marked
285      * &#64;Managed.
286      * @param klass Class to check.
287      * @return true if class is Managed.
288      */
289    public static boolean isManagedClass(Class<?> klass)
290    {
291        return ManagedMarker.class.isAssignableFrom(klass);
292    }
293
294    /** Determines if the given class is partitioned.
295      * Returns true if the &#64;Managed class currently
296      * has a PartitionMapper installed.
297      * @param klass Class to check.
298      * @return true if class is partitioned.
299      * @exception ManagedClassError if klass not Managed.
300      */
301    public static boolean isPartitioned(Class<?> klass)
302    {
303        checkManagedClass(klass, "isPartitioned");
304
305        return NativeRuntime.isPartitioned(klass.getName());
306    }
307
308    /** If the given object is not Managed, throw a new ManagedClassError.
309      */
310    static void checkManagedObject(Object o, String methodName)
311        throws ManagedClassError
312    {
313        if (! isManaged(o))
314        {
315            throw new ManagedClassError("Class "
316                + o.getClass().getName()
317                + " is not Managed. Method "
318                + methodName + "() can only be applied to "
319                + "Managed classes.");
320        }
321    }
322
323    /** If the given class is not Managed, throw a new ManagedClassError.
324      */
325    static void checkManagedClass(Class<?> klass, String methodName)
326        throws ManagedClassError
327    {
328        if (! isManagedClass(klass))
329        {
330            throw new ManagedClassError("Class "
331                + klass.getName()
332                + " is not Managed. Method "
333                + methodName + "() can only be applied to "
334                + "Managed classes.");
335        }
336    }
337
338    /** Return the object reference as a byte array. 
339      * This may return a reference to the actual field, so the result
340      * should be treated as read-only.
341      * @param o Object to get reference from.
342      * @return byte array containing reference.
343      */
344    public static byte[] getObjectReference(Object o)
345    {
346        return ((ManagedMarker)o)._getObjectReference();
347    }
348
349    /** with validation that the caller is a ManagedObject */
350    static byte[] getObjectReference(Object o, final String caller)
351        throws ManagedClassError
352    {
353        checkManagedObject(o, caller);
354
355        final byte[] objRef = ((ManagedMarker)o)._getObjectReference();
356
357        if (objRef == null)
358        {
359            throw new ManagedClassError("Method "
360                + caller + "() called before the Managed object was "
361                + "fully contstructed.");
362        }
363
364        return ((ManagedMarker)o)._getObjectReference();
365    }
366
367    /** Deletes a Managed object.
368      * @param operand Object to delete.
369      * @exception ManagedClassError if operand is not Managed.
370      */
371    public static void delete(Object operand)
372        throws ManagedClassError
373    {
374        NativeRuntime.deleteSMObject(getObjectReference(operand, "delete"));
375    }
376
377    /**
378      * Get a string version of an object reference
379      * <p>
380      * If the object is invalid, an empty string is returned to the caller.
381      * @param operand  Object to get reference for
382      * @return         Stringified object reference
383      * @exception ManagedClassError if operand is not Managed.
384      */
385    public static String objectToReference(Object operand)
386    {
387        String refStr = "";
388
389        if (operand != null)
390        {
391            refStr = NativeRuntime.objectToReference(
392                getObjectReference(operand, "objectToReference"));
393        }
394
395        return refStr;
396    }
397
398    /**
399      * Convert a stringified object reference to an Object
400      * <p>
401      * If the string reference is invalid an empty object
402      * is returned to the caller.
403      * @param refStr   Object reference string
404      * @return     Managed object associated with refStr
405      */
406    public static Object referenceToObject(final String refStr)
407    {
408        return NativeRuntime.referenceToObject(refStr);
409    }
410
411    /** Returns the number of instances of the given class.
412      *<br>
413      * This count does not require transaction locks on the
414      * extent or any of the individual objects in the extent.
415      *<br>
416      * This means the number returned by cardinality() may 
417      * change even as observed within a single transaction.
418      * @param klass extent class - instances of this type,
419      * and any subtypes that extend from this type, will be counted.
420      * @exception ManagedClassError Thrown if <code>klass</code>
421      *   is not marked Managed.
422      * @return Number of instances found.
423      */
424    public static int cardinality(Class<?> klass)
425        throws ManagedClassError
426    {
427        checkManagedClass(klass, "cardinality");
428        return NativeRuntime.cardinality(klass.getName());
429    }
430
431    /** Returns the number of instances of the given class.
432      *<br>
433      * This count does not require transaction locks on the
434      * extent or any of the individual objects in the extent.
435      *<br>
436      * This means the number returned by cardinality() may 
437      * change even as observed within a single transaction.
438      *<p>
439      * The <code>queryScope</code> parameter determines the scope
440      * of the search for object instances.
441      *<p>
442      * @param klass extent class - instances of this type,
443      * and any subtypes that extend from this type, will be counted.
444      * @param queryScope scope of cardinality lookup.
445      * @exception ManagedClassError Thrown if <code>klass</code>
446      *   is not marked Managed.
447      * @exception IllegalArgumentException
448      *  The queryScope failed audit due to a bad node list.
449      * @exception ResourceUnavailableException
450      *  Access to distributed services failed.
451      * @return Number of instances found.
452      * @see com.kabira.platform.QueryScope
453      */
454    public static int cardinality(
455        Class<?> klass,
456        final QueryScope queryScope)
457        throws ManagedClassError,
458                IllegalArgumentException, 
459                ResourceUnavailableException
460    {
461        checkManagedClass(klass, "cardinality");
462
463        if (queryScope.getScope() != QueryScope.Scope.LOCAL)
464        {
465            initializeDistributedQuery();
466            queryScope.audit();
467            if (m_distributedQuery != null)
468            {
469                TypeDescriptor td = TypeDescriptor.getDescriptor(klass);
470                m_distributedQuery.loadObjectExtent(
471                        queryScope,
472                        td.m_domainId,
473                        td.m_typeId,
474                        KeyQuery.mapObjectLock(LockMode.NOLOCK));
475            }
476        }
477
478        return NativeRuntime.cardinality(klass.getName());
479    }
480
481    /** Determines if the backing shared memory object has been deleted.
482      * @param operand Object to check.
483      * @return true if the operand has been deleted, false otherwise. 
484      * @exception ManagedClassError if the operand object is not of a Managed
485      * class.
486      */
487    public static boolean isEmpty(Object operand)
488        throws ManagedClassError
489    {
490        if (operand == null)
491        {
492            return true;
493        }
494
495        return NativeRuntime.isEmpty(getObjectReference(operand, "isEmpty"));
496    }
497
498    /** Internal method - do not use.  Will be removed
499      * in a future release.
500      * @param distributedQuery Distributed query
501      */
502    public static void setDistributedQuery(DistributedQuery distributedQuery)
503    {
504        m_distributedQuery = distributedQuery;
505    }
506
507    //
508    // Package private routine to access a distributedQuery instance.
509    //
510    static DistributedQuery getDistributedQuery()
511    {
512        return m_distributedQuery;
513    }
514
515    //
516    // Package private routine to use reflection to initialize a 
517    // distributedQuery instance.
518    //
519    static synchronized void initializeDistributedQuery()
520    {
521        if (m_distributedQuery == null)
522        {
523            final String pmClass =
524                "com.kabira.platform.highavailability.PartitionManager";
525
526            try
527            {
528                Class<?> cls = Class.forName(pmClass);
529                java.lang.reflect.Method im = cls.getMethod("initialize");
530                im.setAccessible(true);
531                im.invoke(null);
532            }
533            catch (java.lang.Exception ex)
534            {
535                //
536                // ClassNotFoundException is ignored.
537                //
538                // Note that NoSuchMethodException, IllegalAccessException,
539                // and InvocationTargetException are also ignored, but
540                // probably shouldn't be.
541                //
542            }
543        }
544    }
545    static DistributedQuery m_distributedQuery = null;
546
547    /** 
548     * Commit and abort triggers must be enabled in each transaction.
549     * This method enables triggers for all methods in TransactionTriggers
550     * (currently abort and commit).
551     */
552    static void enableTriggers(TransactionTriggers obj)
553    {
554        NativeRuntime.enableTriggers(getObjectReference(obj));
555    }
556
557    /**
558     * Set the context class loader for this thread to this classes class
559     * loader.
560     * NOTE this assumes the context class loader is not set. This should
561     * only be called when attaching a Thread via JNI AttachCurrentThread.
562     */
563    private static void setContextClassLoader()
564    {
565        //
566        // FIX THIS: The IBM JVM asserts on this, commented out for now.
567        //
568        // assert(Thread.currentThread().getContextClassLoader() == null);
569        //
570        assert(ManagedObject.class.getClassLoader() != null);
571
572        Thread.currentThread().setContextClassLoader(
573                    ManagedObject.class.getClassLoader());
574    }
575
576    /** Package private class returned from extent() methods (needed to use the
577      * for-each loop).
578      */
579    static class Extent<T> implements Iterable<T>
580    {
581        private String      m_typeName;
582        private LockMode    m_objectLockMode;
583        Extent(String typeName, LockMode objectLockMode)
584        {
585            m_typeName = typeName;
586            m_objectLockMode = objectLockMode;
587        }
588
589        public Iterator<T> iterator()
590        {
591            return new ExtentIterator<T>(
592                m_typeName,
593                m_objectLockMode);
594        }
595    }
596
597    /** Class wrapping readonly access to a Managed extent.
598      */
599    private static class ExtentIterator<T extends Object>
600        implements java.util.Iterator<T>
601    {
602        private LockMode    m_objectLockMode;
603        private long        m_cHandle;
604        private T           m_next;
605
606        ExtentIterator(String typeName, LockMode objectLock)
607        {
608            m_objectLockMode = objectLock;
609            m_cHandle = _new(typeName, objectLock.ordinal());
610            m_next = null;
611        }
612
613        public boolean hasNext()
614        {
615            if (m_next == null)
616            {
617                getNext();
618            }
619            return (m_next != null);
620        }
621
622        public T next()
623        {
624            if (m_next == null)
625            {
626                getNext();
627            }
628            if (m_next == null)
629            {
630                throw new java.util.NoSuchElementException();
631            }
632
633            T   t = m_next;
634            m_next = null;
635
636            if (m_objectLockMode == LockMode.READLOCK)
637            {
638                Transaction.readLockObject(t);
639            }
640            else if (m_objectLockMode == LockMode.WRITELOCK)
641            {
642                Transaction.writeLockObject(t);
643            }
644            else
645            {
646                assert ( m_objectLockMode == LockMode.NOLOCK );
647            }
648
649            return t;
650        }
651
652        public void remove()
653        {
654            throw new UnsupportedOperationException();
655        }
656
657        protected void finalize()
658        {
659            dispose();
660        }
661
662        private void dispose()
663        {
664            if (m_cHandle != 0)
665            {
666                _delete(m_cHandle);
667            }
668            m_cHandle = 0;
669        }
670
671        private void getNext()
672        {
673            assert ( m_next == null );
674
675            if (m_cHandle != 0)
676            {
677                m_next = _next(m_cHandle);
678
679                //
680                // FIX THIS: Accessing the extent uses a CGObject that does a
681                // detach when _next() returns, allowing the reference count
682                // to go to zero (issue FLUENCY-3716. We use isEmpty() in an
683                // attempt to work around this.
684                // 
685                while (m_next != null && isEmpty(m_next))
686                {
687                    m_next = _next(m_cHandle);
688                }
689
690                if (m_next == null)
691                {
692                    dispose();
693                }
694            }
695        }
696
697        native long _new(String typeName, int objectLock);
698        native T _next(long cHandle);
699        native void _delete(long cHandle);
700    }
701}
702
703/**
704 * Used by the classloader to mark Managed classes as being managed.
705 */
706interface ManagedMarker
707{
708    abstract byte[] _getObjectReference();
709}