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>@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 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 * @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 * @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 @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}