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>@Managed</code> annotation. 022 * <p> 023 * Example: 024 *<pre>@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 < 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>@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 * @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 * @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 @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}