001// 002// Name 003// $RCSfile: Query.java,v $ 004// 005// Copyright 006// Copyright 2014 Cloud Software Group, Inc. ALL RIGHTS RESERVED. 007// Cloud Software Group, Inc. Confidential Information 008// 009// History 010// $Revision: 1.1.2.9.2.1 $ $Date: 2015/10/08 22:36:03 $ 011// 012package com.kabira.store; 013 014import com.kabira.platform.*; 015import com.kabira.platform.annotation.Key; 016import com.kabira.platform.annotation.KeyList; 017import com.kabira.platform.annotation.Managed; 018 019import java.io.IOException; 020import java.io.ObjectInputStream; 021import java.io.ObjectStreamField; 022import java.lang.annotation.Annotation; 023import java.util.ArrayList; 024import java.util.HashSet; 025 026/** 027 * Class for receiving notifications of 028 * {@link com.kabira.platform.KeyQuery} results calls. 029 * <p> 030 * A <code>Query</code> notifier does not directly return results. 031 * The notification provides an opportunity to change the contents of 032 * shared memory (creating and or deleting Managed objects) before 033 * a keyed query is done. 034 * For example, it could be used for creating Managed objects from data 035 * in a database. 036 */ 037@Managed 038public abstract class Query<T> extends QueryBase 039{ 040 private static final int RangeQueryType_RangeQuery = 1; 041 private static final int RangeQueryType_MinimumQuery = 2; 042 private static final int RangeQueryType_MaximumQuery = 3; 043 044 // FIX THIS, DS: these are a copy from KeyQuery.java 045 // but I don't want to make them public there. 046 // 047 // enumeration values for ObjSrv::RangeOperator 048 static final int ObjSrv_EQ = 1; 049 static final int ObjSrv_NEQ = 2; 050 static final int ObjSrv_GTE = 3; 051 static final int ObjSrv_GT = 4; 052 static final int ObjSrv_LTE = 5; 053 static final int ObjSrv_LT = 6; 054 // enumeration values for OSKeyOpt::ObjectLock 055 static final int KeyOptReadLock = 1; 056 static final int KeyOptWriteLock = 2; 057 static final int KeyOptNoLock = 3; 058 059 // force usage of protected ctor 060 private Query() 061 { 062 } 063 064 /** Constructor. 065 * <p> 066 * Creating an instance automatically installs it 067 * as the query notifier for the associated Managed class and key. 068 * <p> 069 * A notifier may only be set on the class where the key is defined. 070 * <p> 071 * The notifier will apply to queries on the class where the key is 072 * defined, and queries on classes which inherit the key. 073 * <p> 074 * If a notifier is already installed for the key, 075 * the new instance replaces the previous instance. 076 * <p> 077 * Deleting a notifier automatically uninstalls it. 078 * <p> 079 * Notifiers are automatically deleted when the JVM shuts down. 080 * Notifiers created in other JVMs are not affected. 081 * <p> 082 * When a {@link KeyQuery} results method is called, the Query notifier 083 * for the key will be called before executing the query. 084 * 085 * @param klass The Managed class where 086 * <code>keyName</code> is defined. 087 * @param keyName The key to receive query notifications for. 088 * 089 * @throws ManagedClassError If <code>klass</code> is not managed. 090 * 091 * @throws KeyUnknownKeyNameError if <code>keyName</code> is not 092 * defined on <code>klass</code>. 093 */ 094 protected Query(final Class<T> klass, final String keyName) 095 throws ManagedClassError, KeyUnknownKeyNameError 096 { 097 audit(klass, keyName); 098 099 m_className = klass.getName(); 100 m_keyName = keyName; 101 install(); 102 NotifierHash.addToHash(this); 103 } 104 105 /** 106 * Return the currently installed Query notifier for 107 * the Managed class and key. 108 * <p> 109 * Inheritance is not examined by this method. 110 * 111 * @param klass Class where key is defined. 112 * @param key Name of key. 113 * @return Query or null. 114 * @throws ManagedClassError If <code>klass</code> is not managed. 115 * @throws KeyUnknownKeyNameError If <code>keyName</code> is not 116 * defined on <code>klass</code>. 117 */ 118 public static Query<?> getNotifier( 119 final Class<?> klass, final String key) 120 { 121 audit(klass, key); 122 return (Query)StoreUtil.getQueryNotifier(klass.getName(), key); 123 } 124 125 /** 126 * Get the name of the key for this Query notifier. 127 * @return Key name. 128 */ 129 public final String getKeyName() 130 { 131 return m_keyName; 132 } 133 134 /** 135 * Notifier method for {@link KeyQuery} results calls. 136 * <p> 137 * Called by the system when following KeyQuery methods are called, 138 * but before they are executed: 139 * <p> 140 * {@link KeyQuery#getOrCreateSingleResult} - for unique keys 141 * <p> 142 * {@link KeyQuery#getSingleResult} - for unique keys 143 * <p> 144 * {@link KeyQuery#getResults} - for non unique keys 145 * <p> 146 * {@link com.kabira.platform.KeyOrderedBy} does not need to be 147 * considered by the notifier. 148 * Result set ordering is handled by the runtime when the 149 * query executes. 150 * <p> 151 * Will be called on each of the nodes in the 152 * {@link com.kabira.platform.QueryScope} of the KeyQuery results call. 153 * <p> 154 * Work done within Query.query() will not 155 * generate calls to com.kabira.store notifiers on the local node. 156 * <p> 157 * <b>Note:</b> work done within the Query.query() method will generate 158 * calls to com.kabira.store notifiers on remote nodes. 159 * 160 * @param klass The Managed class for the query results call. 161 * This may be a subtype of the class 162 * associated with the notifier. 163 * @param lockMode The lock mode for the query results. The notifier 164 * implementation should attempt to honor this lock mode, 165 * but there is no enforcement. 166 * @param queryData The query data, ordered as the caller added it 167 * to the {@link com.kabira.platform.KeyFieldValueList} 168 * or {@link com.kabira.platform.KeyFieldValueRangeList} 169 * for the query. 170 * See {@link KeyField}. 171 */ 172 public abstract void query( 173 final Class<? extends T> klass, 174 final LockMode lockMode, 175 final ArrayList<KeyField> queryData); 176 177 /** 178 * Notifier method for {@link KeyQuery#getMinimumResult} calls. 179 * <p> 180 * Called by the system when KeyQuery.getMinimumResult 181 * is called, but before it is executed. 182 * <p> 183 * Will be called on each of the nodes in the 184 * {@link com.kabira.platform.QueryScope} of getMinimumResult 185 * call. 186 * <p> 187 * Work done within Query.queryMinimum() will not 188 * generate calls to com.kabira.store notifiers on the local node. 189 * <p> 190 * <b>Note:</b> work done within Query.queryMinimum() will generate 191 * calls to com.kabira.store notifiers on remote nodes. 192 * 193 * @param klass The Managed class for the getMinimumResult call. 194 * This may be a subtype of the class associated 195 * with the notifier. 196 * @param lockMode The lock mode for the query result. The notifier 197 * implementation should attempt to honor this lock mode, 198 * but there is no enforcement. 199 * @param queryData The query data, ordered as the caller added it 200 * to the {@link com.kabira.platform.KeyFieldValueList} 201 * or {@link com.kabira.platform.KeyFieldValueRangeList} 202 * for the query. 203 * See {@link KeyField}. 204 */ 205 public abstract void queryMinimum( 206 final Class<? extends T> klass, 207 final LockMode lockMode, 208 final ArrayList<KeyField> queryData); 209 210 /** 211 * Notifier method for {@link KeyQuery#getMaximumResult} calls. 212 * <p> 213 * Called by the system when KeyQuery.getMaximumResult 214 * is called, but before it is executed. 215 * <p> 216 * Will be called on each of the nodes in the 217 * {@link com.kabira.platform.QueryScope} of getMaximumResult 218 * call. 219 * <p> 220 * Work done within Query.queryMaximum() will not 221 * generate calls to com.kabira.store notifiers on the local node. 222 * <p> 223 * <b>Note:</b> work done within Query.queryMaximum() will generate 224 * calls to com.kabira.store notifiers on remote nodes. 225 * 226 * @param klass The Managed class for the getMaximumResult call. 227 * This may be a subtype of the class associated with 228 * the notifier. 229 * @param lockMode The lock mode for the query result. The notifier 230 * implementation should attempt to honor this lock mode, 231 * but there is no enforcement. 232 * @param queryData The query data, ordered as the caller added it 233 * to the {@link com.kabira.platform.KeyFieldValueList} 234 * or {@link com.kabira.platform.KeyFieldValueRangeList} 235 * for the query. 236 * See {@link KeyField}. 237 */ 238 public abstract void queryMaximum( 239 final Class<? extends T> klass, 240 final LockMode lockMode, 241 final ArrayList<KeyField> queryData); 242 243 void querySimpleInternal( 244 final Class<? extends T> klass, 245 final int objSrvLockMode, 246 final ManagedObjectInputStream keyStream) 247 throws IOException, ClassNotFoundException 248 { 249 ManagedObjectStreamClass keyDesc = keyStream.getStreamClass(); 250 LockMode lockMode = mapLockMode(objSrvLockMode); 251 ArrayList<KeyField> queryData = buildQueryData(keyDesc, keyStream); 252 253 query(klass, lockMode, queryData); 254 } 255 256 void queryOrderedInternal( 257 final Class<? extends T> klass, 258 final int objSrvLockMode, 259 final int rangeQueryType, 260 final ManagedObjectStreamClass keyDesc1, 261 final ManagedObjectInputStream keyStream1, 262 final int rop1, 263 final ManagedObjectStreamClass keyDesc2, 264 final ManagedObjectInputStream keyStream2, 265 final int rop2, 266 final ManagedObjectStreamClass keyDesc, 267 final KeyFilter[] filters) 268 throws IOException, ClassNotFoundException 269 { 270 LockMode lockMode = mapLockMode(objSrvLockMode); 271 ArrayList<KeyField> queryData = buildQueryData( 272 keyDesc1, keyStream1, rop1, keyDesc2, 273 keyStream2, rop2, keyDesc, filters); 274 275 if (rangeQueryType == RangeQueryType_RangeQuery) 276 { 277 query(klass, lockMode, queryData); 278 } 279 else if (rangeQueryType == RangeQueryType_MinimumQuery) 280 { 281 queryMinimum(klass, lockMode, queryData); 282 } 283 else 284 { 285 assert rangeQueryType == RangeQueryType_MaximumQuery 286 : rangeQueryType + " != " + RangeQueryType_MaximumQuery; 287 queryMaximum(klass, lockMode, queryData); 288 } 289 } 290 291 private static void audit(final Class<?> klass, final String keyName) 292 { 293 if (! ManagedObject.isManagedClass(klass)) 294 { 295 throw new ManagedClassError( 296 klass.getName() + " is not @Managed"); 297 } 298 299 for (Annotation annotation : klass.getDeclaredAnnotations()) 300 { 301 Class<?> annotationClass = annotation.annotationType(); 302 303 if (annotationClass == Key.class) 304 { 305 if (((Key)annotation).name().equals(keyName)) 306 { 307 return; 308 } 309 } 310 else if (annotationClass == KeyList.class) 311 { 312 KeyList keyList = (KeyList)annotation; 313 314 for (Key key : keyList.keys()) 315 { 316 if (key.name().equals(keyName)) 317 { 318 return; 319 } 320 } 321 } 322 } 323 324 throw new KeyUnknownKeyNameError( 325 "Key " + keyName + " is not defined on " 326 + "Managed class " + klass.getName()); 327 } 328 329 private LockMode mapLockMode(final int objSrvLockMode) 330 { 331 switch (objSrvLockMode) 332 { 333 case KeyOptNoLock: 334 return LockMode.NOLOCK; 335 case KeyOptReadLock: 336 return LockMode.READLOCK; 337 } 338 339 assert objSrvLockMode == KeyOptWriteLock; 340 341 return LockMode.WRITELOCK; 342 } 343 344 static KeyComparisonOperator mapComparisonOp(final int rop) 345 { 346 switch (rop) 347 { 348 case ObjSrv_EQ: 349 return KeyComparisonOperator.EQ; 350 case ObjSrv_GT: 351 return KeyComparisonOperator.GT; 352 case ObjSrv_GTE: 353 return KeyComparisonOperator.GTE; 354 case ObjSrv_LT: 355 return KeyComparisonOperator.LT; 356 case ObjSrv_LTE: 357 return KeyComparisonOperator.LTE; 358 } 359 360 assert rop == ObjSrv_NEQ; 361 return KeyComparisonOperator.NEQ; 362 } 363 364 // filter ops are the opposite of comparison ops 365 static KeyComparisonOperator mapFilterOp(final int rop) 366 { 367 switch (rop) 368 { 369 case ObjSrv_EQ: 370 return KeyComparisonOperator.NEQ; 371 case ObjSrv_GT: 372 return KeyComparisonOperator.LTE; 373 case ObjSrv_GTE: 374 return KeyComparisonOperator.LT; 375 case ObjSrv_LT: 376 return KeyComparisonOperator.GTE; 377 case ObjSrv_LTE: 378 return KeyComparisonOperator.GT; 379 } 380 381 assert rop == ObjSrv_NEQ; 382 return KeyComparisonOperator.EQ; 383 } 384 385 private ArrayList<KeyField> buildQueryData( 386 final ManagedObjectStreamClass keyDescription, 387 final ManagedObjectInputStream keyStream) 388 throws IOException, ClassNotFoundException 389 { 390 ArrayList<KeyField> queryData = new ArrayList<KeyField>(); 391 392 ObjectInputStream.GetField gf = keyStream.readFields(); 393 394 for (ObjectStreamField field : keyDescription.getFields()) 395 { 396 final String fieldName = field.getName(); 397 final Object value = gf.get(fieldName, null); 398 final KeyField keyField = 399 new KeyField(fieldName, value, KeyComparisonOperator.EQ); 400 401 queryData.add(keyField); 402 } 403 404 return queryData; 405 } 406 407 private ArrayList<KeyField> buildQueryData( 408 final ManagedObjectStreamClass keyDescription1, 409 final ManagedObjectInputStream keyStream1, 410 final int rop1, 411 final ManagedObjectStreamClass keyDescription2, 412 final ManagedObjectInputStream keyStream2, 413 final int rop2, 414 final ManagedObjectStreamClass keyDescription, 415 final KeyFilter[] filters) 416 throws IOException, ClassNotFoundException 417 { 418 ArrayList<KeyField> queryData = new ArrayList<KeyField>(); 419 ObjectInputStream.GetField gf; 420 KeyComparisonOperator rop; 421 422 if (keyStream1 != null) 423 { 424 gf = keyStream1.readFields(); 425 rop = mapComparisonOp(rop1); 426 427 for (ObjectStreamField field : keyDescription1.getFields()) 428 { 429 final String fieldName = field.getName(); 430 queryData.add(new KeyField( 431 fieldName, gf.get(fieldName, null), rop)); 432 } 433 } 434 435 if (keyStream2 != null) 436 { 437 gf = keyStream2.readFields(); 438 rop = mapComparisonOp(rop2); 439 440 for (ObjectStreamField field : keyDescription2.getFields()) 441 { 442 final String fieldName = field.getName(); 443 queryData.add(new KeyField( 444 fieldName, gf.get(fieldName, null), rop)); 445 } 446 } 447 448 if (filters != null) 449 { 450 for (KeyFilter filter : filters) 451 { 452 gf = filter.m_keyData.readFields(); 453 454 int keyDepth = filter.m_keyDepth; 455 456 final ObjectStreamField[] fields = keyDescription.getFields(); 457 458 assert keyDepth < fields.length; 459 final ObjectStreamField field = fields[keyDepth]; 460 461 final String fieldName = field.getName(); 462 queryData.add(new KeyField( 463 fieldName, gf.get(fieldName, null), filter.m_rop)); 464 } 465 } 466 467 return queryData; 468 } 469 470 static 471 { 472 Runtime.getRuntime().addShutdownHook(new Thread() 473 { 474 @Override 475 public void run() 476 { 477 new Transaction("Query notifier shutdown") 478 { 479 @Override 480 protected void run() 481 { 482 NotifierHash.clearNotifiers(); 483 } 484 }.execute(); 485 } 486 }); 487 } 488 489 private static class NotifierHash 490 { 491 static final HashSet<Query<?>> m_installedNotifiers 492 = new HashSet<Query<?>>(); 493 494 static void addToHash(final Query<?> notifier) 495 { 496 m_installedNotifiers.add(notifier); 497 } 498 499 static void clearNotifiers() 500 { 501 for (Query<?> query : m_installedNotifiers) 502 { 503 if (! ManagedObject.isEmpty(query)) 504 { 505 ManagedObject.delete(query); 506 } 507 } 508 } 509 } 510}