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 $ $Date: 2015/01/27 03:25:12 $
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 Managed object 
247      * @param o2 Managed object
248      * @return true if the two instances refer to the same Managed object.
249      * @exception ManagedClassError if o1 or o2 is not Managed.
250      */
251    public static boolean equals(Object o1, Object o2)
252        throws ManagedClassError
253    {
254        if (o1 == o2)
255        {
256            return true;
257        }
258
259        if ((o1 == null) || (o2 == null))
260        {
261            return false;
262        }
263
264        return java.util.Arrays.equals(
265            getObjectReference(o1, "equals"),
266            getObjectReference(o2, "equals"));
267    }
268
269    /** Determines if the given object is an instance of a Managed class.
270      * Returns true only if the object, or a superclass, is marked
271      * &#64;Managed.
272      * @param o Object to check.
273      * @return true if object is a Managed object.
274      */
275    public static boolean isManaged(Object o)
276    {
277        return o instanceof ManagedMarker;
278    }
279
280    /** Determines if the given class is Managed.
281      * Returns true only if the class, or a superclass, is marked
282      * &#64;Managed.
283      * @param klass Class to check.
284      * @return true if class is Managed.
285      */
286    public static boolean isManagedClass(Class<?> klass)
287    {
288        return ManagedMarker.class.isAssignableFrom(klass);
289    }
290
291    /** Determines if the given class is partitioned.
292      * Returns true if the &#64;Managed class currently
293      * has a PartitionMapper installed.
294      * @param klass Class to check.
295      * @return true if class is partitioned.
296      * @exception ManagedClassError if klass not Managed.
297      */
298    public static boolean isPartitioned(Class<?> klass)
299    {
300        checkManagedClass(klass, "isPartitioned");
301
302        return NativeRuntime.isPartitioned(klass.getName());
303    }
304
305    /** If the given object is not Managed, throw a new ManagedClassError.
306      */
307    static void checkManagedObject(Object o, String methodName)
308        throws ManagedClassError
309    {
310        if (! isManaged(o))
311        {
312            throw new ManagedClassError("Class "
313                + o.getClass().getName()
314                + " is not Managed. Method "
315                + methodName + "() can only be applied to "
316                + "Managed classes.");
317        }
318    }
319
320    /** If the given class is not Managed, throw a new ManagedClassError.
321      */
322    static void checkManagedClass(Class<?> klass, String methodName)
323        throws ManagedClassError
324    {
325        if (! isManagedClass(klass))
326        {
327            throw new ManagedClassError("Class "
328                + klass.getName()
329                + " is not Managed. Method "
330                + methodName + "() can only be applied to "
331                + "Managed classes.");
332        }
333    }
334
335    /** Return the object reference as a byte array. 
336      * This may return a reference to the actual field, so the result
337      * should be treated as read-only.
338      * @param o Object to get reference from.
339      * @return byte array containing reference.
340      */
341    public static byte[] getObjectReference(Object o)
342    {
343        return ((ManagedMarker)o)._getObjectReference();
344    }
345
346    /** with validation that the caller is a ManagedObject */
347    static byte[] getObjectReference(Object o, final String caller)
348        throws ManagedClassError
349    {
350        checkManagedObject(o, caller);
351
352        final byte[] objRef = ((ManagedMarker)o)._getObjectReference();
353
354        if (objRef == null)
355        {
356            throw new ManagedClassError("Method "
357                + caller + "() called before the Managed object was "
358                + "fully contstructed.");
359        }
360
361        return ((ManagedMarker)o)._getObjectReference();
362    }
363
364    /** Deletes a Managed object.
365      * @param operand Object to delete.
366      * @exception ManagedClassError if operand is not Managed.
367      */
368    public static void delete(Object operand)
369        throws ManagedClassError
370    {
371        NativeRuntime.deleteSMObject(getObjectReference(operand, "delete"));
372    }
373
374    /**
375      * Get a string version of an object reference
376      * <p>
377      * If the object is invalid, an empty string is returned to the caller.
378      * @param operand  Object to get reference for
379      * @return         Stringified object reference
380      * @exception ManagedClassError if operand is not Managed.
381      */
382    public static String objectToReference(Object operand)
383    {
384        String refStr = "";
385
386        if (operand != null)
387        {
388            refStr = NativeRuntime.objectToReference(
389                getObjectReference(operand, "objectToReference"));
390        }
391
392        return refStr;
393    }
394
395    /**
396      * Convert a stringified object reference to an Object
397      * <p>
398      * If the string reference is invalid an empty object
399      * is returned to the caller.
400      * @param refStr   Object reference string
401      * @return     Managed object associated with refStr
402      */
403    public static Object referenceToObject(final String refStr)
404    {
405        return NativeRuntime.referenceToObject(refStr);
406    }
407
408    /** Returns the number of instances of the given class.
409      *<br>
410      * This count does not require transaction locks on the
411      * extent or any of the individual objects in the extent.
412      *<br>
413      * This means the number returned by cardinality() may 
414      * change even as observed within a single transaction.
415      * @param klass extent class - instances of this type,
416      * and any subtypes that extend from this type, will be counted.
417      * @exception ManagedClassError Thrown if <code>klass</code>
418      *   is not marked Managed.
419      * @return Number of instances found.
420      */
421    public static int cardinality(Class<?> klass)
422        throws ManagedClassError
423    {
424        checkManagedClass(klass, "cardinality");
425        return NativeRuntime.cardinality(klass.getName());
426    }
427
428    /** Returns the number of instances of the given class.
429      *<br>
430      * This count does not require transaction locks on the
431      * extent or any of the individual objects in the extent.
432      *<br>
433      * This means the number returned by cardinality() may 
434      * change even as observed within a single transaction.
435      *<p>
436      * The <code>queryScope</code> parameter determines the scope
437      * of the search for object instances.
438      *<p>
439      * @param klass extent class - instances of this type,
440      * and any subtypes that extend from this type, will be counted.
441      * @param queryScope scope of cardinality lookup.
442      * @exception ManagedClassError Thrown if <code>klass</code>
443      *   is not marked Managed.
444      * @exception IllegalArgumentException
445      *  The queryScope failed audit due to a bad node list.
446      * @exception ResourceUnavailableException
447      *  Access to distributed services failed.
448      * @return Number of instances found.
449      * @see com.kabira.platform.QueryScope
450      */
451    public static int cardinality(
452        Class<?> klass,
453        final QueryScope queryScope)
454        throws ManagedClassError,
455                IllegalArgumentException, 
456                ResourceUnavailableException
457    {
458        checkManagedClass(klass, "cardinality");
459
460        if (queryScope.getScope() != QueryScope.Scope.LOCAL)
461        {
462            initializeDistributedQuery();
463            queryScope.audit();
464            if (m_distributedQuery != null)
465            {
466                TypeDescriptor td = TypeDescriptor.getDescriptor(klass);
467                m_distributedQuery.loadObjectExtent(
468                        queryScope,
469                        td.m_domainId,
470                        td.m_typeId,
471                        KeyQuery.mapObjectLock(LockMode.NOLOCK));
472            }
473        }
474
475        return NativeRuntime.cardinality(klass.getName());
476    }
477
478    /** Determines if the backing shared memory object has been deleted.
479      * @param operand Object to check.
480      * @return true if the operand has been deleted, false otherwise. 
481      * @exception ManagedClassError if the operand object is not of a Managed
482      * class.
483      */
484    public static boolean isEmpty(Object operand)
485        throws ManagedClassError
486    {
487        if (operand == null)
488        {
489            return true;
490        }
491
492        return NativeRuntime.isEmpty(getObjectReference(operand, "isEmpty"));
493    }
494
495    /** Internal method - do not use.  Will be removed
496      * in a future release.
497      * @param distributedQuery Distributed query
498      */
499    public static void setDistributedQuery(DistributedQuery distributedQuery)
500    {
501        m_distributedQuery = distributedQuery;
502    }
503
504    //
505    // Package private routine to access a distributedQuery instance.
506    //
507    static DistributedQuery getDistributedQuery()
508    {
509        return m_distributedQuery;
510    }
511
512    //
513    // Package private routine to use reflection to initialize a 
514    // distributedQuery instance.
515    //
516    static synchronized void initializeDistributedQuery()
517    {
518        if (m_distributedQuery == null)
519        {
520            final String pmClass =
521                "com.kabira.platform.highavailability.PartitionManager";
522
523            try
524            {
525                Class<?> cls = Class.forName(pmClass);
526                java.lang.reflect.Method im = cls.getMethod("initialize");
527                im.setAccessible(true);
528                im.invoke(null);
529            }
530            catch (java.lang.Exception ex)
531            {
532                //
533                // ClassNotFoundException is ignored.
534                //
535                // Note that NoSuchMethodException, IllegalAccessException,
536                // and InvocationTargetException are also ignored, but
537                // probably shouldn't be.
538                //
539            }
540        }
541    }
542    static DistributedQuery m_distributedQuery = null;
543
544    /** 
545     * Commit and abort triggers must be enabled in each transaction.
546     * This method enables triggers for all methods in TransactionTriggers
547     * (currently abort and commit).
548     */
549    static void enableTriggers(TransactionTriggers obj)
550    {
551        NativeRuntime.enableTriggers(getObjectReference(obj));
552    }
553
554    /**
555     * Set the context class loader for this thread to this classes class
556     * loader.
557     * NOTE this assumes the context class loader is not set. This should
558     * only be called when attaching a Thread via JNI AttachCurrentThread.
559     */
560    private static void setContextClassLoader()
561    {
562        //
563        // FIX THIS: The IBM JVM asserts on this, commented out for now.
564        //
565        // assert(Thread.currentThread().getContextClassLoader() == null);
566        //
567        assert(ManagedObject.class.getClassLoader() != null);
568
569        Thread.currentThread().setContextClassLoader(
570                    ManagedObject.class.getClassLoader());
571    }
572
573    /** Package private class returned from extent() methods (needed to use the
574      * for-each loop).
575      */
576    static class Extent<T> implements Iterable<T>
577    {
578        private String      m_typeName;
579        private LockMode    m_objectLockMode;
580        Extent(String typeName, LockMode objectLockMode)
581        {
582            m_typeName = typeName;
583            m_objectLockMode = objectLockMode;
584        }
585
586        public Iterator<T> iterator()
587        {
588            return new ExtentIterator<T>(
589                m_typeName,
590                m_objectLockMode);
591        }
592    }
593
594    /** Class wrapping readonly access to a Managed extent.
595      */
596    private static class ExtentIterator<T extends Object>
597        implements java.util.Iterator<T>
598    {
599        private LockMode    m_objectLockMode;
600        private long        m_cHandle;
601        private T           m_next;
602
603        ExtentIterator(String typeName, LockMode objectLock)
604        {
605            m_objectLockMode = objectLock;
606            m_cHandle = _new(typeName, objectLock.ordinal());
607            m_next = null;
608        }
609
610        public boolean hasNext()
611        {
612            if (m_next == null)
613            {
614                getNext();
615            }
616            return (m_next != null);
617        }
618
619        public T next()
620        {
621            if (m_next == null)
622            {
623                getNext();
624            }
625            if (m_next == null)
626            {
627                throw new java.util.NoSuchElementException();
628            }
629
630            T   t = m_next;
631            m_next = null;
632
633            if (m_objectLockMode == LockMode.READLOCK)
634            {
635                Transaction.readLockObject(t);
636            }
637            else if (m_objectLockMode == LockMode.WRITELOCK)
638            {
639                Transaction.writeLockObject(t);
640            }
641            else
642            {
643                assert ( m_objectLockMode == LockMode.NOLOCK );
644            }
645
646            return t;
647        }
648
649        public void remove()
650        {
651            throw new UnsupportedOperationException();
652        }
653
654        protected void finalize()
655        {
656            dispose();
657        }
658
659        private void dispose()
660        {
661            if (m_cHandle != 0)
662            {
663                _delete(m_cHandle);
664            }
665            m_cHandle = 0;
666        }
667
668        private void getNext()
669        {
670            assert ( m_next == null );
671
672            if (m_cHandle != 0)
673            {
674                m_next = _next(m_cHandle);
675
676                //
677                // FIX THIS: Accessing the extent uses a CGObject that does a
678                // detach when _next() returns, allowing the reference count
679                // to go to zero (issue FLUENCY-3716. We use isEmpty() in an
680                // attempt to work around this.
681                // 
682                while (m_next != null && isEmpty(m_next))
683                {
684                    m_next = _next(m_cHandle);
685                }
686
687                if (m_next == null)
688                {
689                    dispose();
690                }
691            }
692        }
693
694        native long _new(String typeName, int objectLock);
695        native T _next(long cHandle);
696        native void _delete(long cHandle);
697    }
698}
699
700/**
701 * Used by the classloader to mark Managed classes as being managed.
702 */
703interface ManagedMarker
704{
705    abstract byte[] _getObjectReference();
706}