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 $ $Date: 2014/11/26 22:22:12 $ 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 keyDesc, 261 final ManagedObjectInputStream keyStream1, 262 final int rop1, 263 final ManagedObjectInputStream keyStream2, 264 final int rop2, 265 final KeyFilter[] filters) 266 throws IOException, ClassNotFoundException 267 { 268 LockMode lockMode = mapLockMode(objSrvLockMode); 269 ArrayList<KeyField> queryData = buildQueryData( 270 keyDesc, keyStream1, rop1, keyStream2, rop2, filters); 271 272 if (rangeQueryType == RangeQueryType_RangeQuery) 273 { 274 query(klass, lockMode, queryData); 275 } 276 else if (rangeQueryType == RangeQueryType_MinimumQuery) 277 { 278 queryMinimum(klass, lockMode, queryData); 279 } 280 else 281 { 282 assert rangeQueryType == RangeQueryType_MaximumQuery 283 : rangeQueryType + " != " + RangeQueryType_MaximumQuery; 284 queryMaximum(klass, lockMode, queryData); 285 } 286 } 287 288 private static void audit(final Class<?> klass, final String keyName) 289 { 290 if (! ManagedObject.isManagedClass(klass)) 291 { 292 throw new ManagedClassError( 293 klass.getName() + " is not @Managed"); 294 } 295 296 for (Annotation annotation : klass.getDeclaredAnnotations()) 297 { 298 Class<?> annotationClass = annotation.annotationType(); 299 300 if (annotationClass == Key.class) 301 { 302 if (((Key)annotation).name().equals(keyName)) 303 { 304 return; 305 } 306 } 307 else if (annotationClass == KeyList.class) 308 { 309 KeyList keyList = (KeyList)annotation; 310 311 for (Key key : keyList.keys()) 312 { 313 if (key.name().equals(keyName)) 314 { 315 return; 316 } 317 } 318 } 319 } 320 321 throw new KeyUnknownKeyNameError( 322 "Key " + keyName + " is not defined on " 323 + "Managed class " + klass.getName()); 324 } 325 326 private LockMode mapLockMode(final int objSrvLockMode) 327 { 328 switch (objSrvLockMode) 329 { 330 case KeyOptNoLock: 331 return LockMode.NOLOCK; 332 case KeyOptReadLock: 333 return LockMode.READLOCK; 334 } 335 336 assert objSrvLockMode == KeyOptWriteLock; 337 338 return LockMode.WRITELOCK; 339 } 340 341 static KeyComparisonOperator mapComparisonOp(final int rop) 342 { 343 switch (rop) 344 { 345 case ObjSrv_EQ: 346 return KeyComparisonOperator.EQ; 347 case ObjSrv_GT: 348 return KeyComparisonOperator.GT; 349 case ObjSrv_GTE: 350 return KeyComparisonOperator.GTE; 351 case ObjSrv_LT: 352 return KeyComparisonOperator.LT; 353 case ObjSrv_LTE: 354 return KeyComparisonOperator.LTE; 355 } 356 357 assert rop == ObjSrv_NEQ; 358 return KeyComparisonOperator.NEQ; 359 } 360 361 // filter ops are the opposite of comparison ops 362 static KeyComparisonOperator mapFilterOp(final int rop) 363 { 364 switch (rop) 365 { 366 case ObjSrv_EQ: 367 return KeyComparisonOperator.NEQ; 368 case ObjSrv_GT: 369 return KeyComparisonOperator.LTE; 370 case ObjSrv_GTE: 371 return KeyComparisonOperator.LT; 372 case ObjSrv_LT: 373 return KeyComparisonOperator.GTE; 374 case ObjSrv_LTE: 375 return KeyComparisonOperator.GT; 376 } 377 378 assert rop == ObjSrv_NEQ; 379 return KeyComparisonOperator.EQ; 380 } 381 382 private ArrayList<KeyField> buildQueryData( 383 final ManagedObjectStreamClass keyDescription, 384 final ManagedObjectInputStream keyStream) 385 throws IOException, ClassNotFoundException 386 { 387 ArrayList<KeyField> queryData = new ArrayList<KeyField>(); 388 389 ObjectInputStream.GetField gf = keyStream.readFields(); 390 391 for (ObjectStreamField field : keyDescription.getFields()) 392 { 393 final String fieldName = field.getName(); 394 final Object value = gf.get(fieldName, null); 395 final KeyField keyField = 396 new KeyField(fieldName, value, KeyComparisonOperator.EQ); 397 398 queryData.add(keyField); 399 } 400 401 return queryData; 402 } 403 404 private ArrayList<KeyField> buildQueryData( 405 final ManagedObjectStreamClass keyDescription, 406 final ManagedObjectInputStream keyStream1, 407 final int rop1, 408 final ManagedObjectInputStream keyStream2, 409 final int rop2, 410 final KeyFilter[] filters) 411 throws IOException, ClassNotFoundException 412 { 413 ArrayList<KeyField> queryData = new ArrayList<KeyField>(); 414 ObjectInputStream.GetField gf; 415 KeyComparisonOperator rop; 416 417 if (keyStream1 != null) 418 { 419 gf = keyStream1.readFields(); 420 rop = mapComparisonOp(rop1); 421 422 for (ObjectStreamField field : keyDescription.getFields()) 423 { 424 final String fieldName = field.getName(); 425 queryData.add(new KeyField( 426 fieldName, gf.get(fieldName, null), rop)); 427 } 428 } 429 430 if (keyStream2 != null) 431 { 432 gf = keyStream2.readFields(); 433 rop = mapComparisonOp(rop2); 434 435 for (ObjectStreamField field : keyDescription.getFields()) 436 { 437 final String fieldName = field.getName(); 438 queryData.add(new KeyField( 439 fieldName, gf.get(fieldName, null), rop)); 440 } 441 } 442 443 if (filters != null) 444 { 445 for (KeyFilter filter : filters) 446 { 447 gf = filter.m_keyData.readFields(); 448 449 for (ObjectStreamField field : keyDescription.getFields()) 450 { 451 final String fieldName = field.getName(); 452 queryData.add(new KeyField( 453 fieldName, gf.get(fieldName, null), filter.m_rop)); 454 } 455 } 456 } 457 458 return queryData; 459 } 460 461 static 462 { 463 Runtime.getRuntime().addShutdownHook(new Thread() 464 { 465 @Override 466 public void run() 467 { 468 new Transaction("Query notifier shutdown") 469 { 470 @Override 471 protected void run() 472 { 473 NotifierHash.clearNotifiers(); 474 } 475 }.execute(); 476 } 477 }); 478 } 479 480 private static class NotifierHash 481 { 482 static final HashSet<Query<?>> m_installedNotifiers 483 = new HashSet<Query<?>>(); 484 485 static void addToHash(final Query<?> notifier) 486 { 487 m_installedNotifiers.add(notifier); 488 } 489 490 static void clearNotifiers() 491 { 492 for (Query<?> query : m_installedNotifiers) 493 { 494 if (! ManagedObject.isEmpty(query)) 495 { 496 ManagedObject.delete(query); 497 } 498 } 499 } 500 } 501 502}