001/* 002 * $RCSfile: Concept.java,v $ 003 * $Revision: 1.43.2.4 $ 004 * $Date: 2015/05/04 16:22:49 $ 005 * 006 * Copyright 2012, 2013 Cloud Software Group, Inc. ALL RIGHTS RESERVED. 007 * Cloud Software Group, Inc. Confidential Information 008 */ 009package com.tibco.xp.runtime; 010 011import com.kabira.platform.KeyFieldValueList; 012import com.kabira.platform.KeyManager; 013import com.kabira.platform.KeyQuery; 014import com.kabira.platform.KeyOrderedBy; 015import com.kabira.platform.LockMode; 016import com.kabira.platform.ManagedObject; 017import com.kabira.platform.ObjectNotUniqueError; 018import com.kabira.platform.ResourceUnavailableException; 019import com.kabira.platform.annotation.Managed; 020import com.tibco.cep.kernel.model.knowledgebase.DuplicateExtIdException; 021import com.tibco.cep.kernel.service.logging.Level; 022import com.tibco.cep.runtime.model.element.Property; 023import com.tibco.cep.runtime.model.element.PropertyArray; 024import com.tibco.cep.runtime.model.element.PropertyAtom; 025import com.tibco.cep.runtime.model.element.PropertyAtomConcept; 026import com.tibco.cep.runtime.model.element.impl.ConceptImpl; 027import com.tibco.cep.runtime.model.element.impl.ManagedObjectLockType; 028import com.tibco.cep.runtime.model.element.impl.property.simple.PropertyArrayConceptReferenceSimple; 029import com.tibco.cep.runtime.model.element.impl.property.simple.PropertyArrayContainedConceptSimple; 030 031import java.util.Date; 032import java.util.HashMap; 033import java.util.Set; 034 035/** 036 * Concept base class 037 */ 038@Managed 039public abstract class Concept extends Entity 040{ 041 /** 042 * Lookup operation type - unique, or not. 043 */ 044 protected enum LookupType 045 { 046 /** 047 * Perform a unique key lookup 048 */ 049 UNIQUE_LOOKUP, 050 /** 051 * Perform a non-unique key lookup 052 */ 053 NONUNIQUE_LOOKUP 054 }; 055 056 /** 057 * Java constructor 058 */ 059 protected Concept() 060 { 061 } 062 063 /** 064 * Rule or Rule Function constructor 065 * @param id Unique identifier 066 */ 067 protected Concept(long id) 068 { 069 register(id); 070 } 071 072 /** 073 * Assert a concept and start a run-to-completion cycle. 074 * <p> 075 * This starts a new run-to-completion (RTC) cycle. A 076 * new transaction is started if one is not already active. 077 * The RTC cycle is complete when this 078 * method returns. RETE working memory is cleared at the end 079 * of the RTC cycle. 080 * <p> 081 * If the concept was previously loaded into working memory 082 * this method quietly does nothing. 083 * @throws ObjectNotUniqueError Duplicate external identifier 084 * @throws ResourceUnavailableException Rules engine not running 085 */ 086 public final void assertConcept() 087 throws ObjectNotUniqueError, ResourceUnavailableException 088 { 089 try 090 { 091 assertEntity(true); 092 } 093 catch (DuplicateExtIdException e) 094 { 095 throw new ObjectNotUniqueError(e.getLocalizedMessage()); 096 } 097 } 098 099 /** 100 * Assert a concept, but do not start a run-to-completion cycle. 101 * <p> 102 * Assert a concept into the RETE, but do not start a 103 * run-to-completion cycle. 104 * <p> 105 * If the concept was previously loaded into working memory 106 * this method quietly does nothing. 107 * @throws ObjectNotUniqueError Duplicate external identifier 108 * @throws ResourceUnavailableException Rules engine not running 109 */ 110 public final void assertConceptWithoutRunToCompletion() 111 throws ObjectNotUniqueError, ResourceUnavailableException 112 { 113 try 114 { 115 assertEntity(false); 116 } 117 catch (DuplicateExtIdException e) 118 { 119 throw new ObjectNotUniqueError(e.getLocalizedMessage()); 120 } 121 } 122 123 @Override 124 public String toString() 125 { 126 StringBuilder value = new StringBuilder(super.toString()); 127 128 value.append("&objectreference="); 129 value.append(ManagedObject.objectToReference(this)); 130 return value.toString(); 131 } 132 133 /** 134 * Execute a query. 135 * <p> 136 * Unique queries return a concept or, null if not found. The concept is 137 * added to working memory in the current RTC. 138 * <p> 139 * Non-unique or ordered queries return an iterator. Concepts 140 * returned in the iterator or not added to working memory 141 * in the current RTC until they are accessed via the returned 142 * iterator. 143 * @param klass Concept class. 144 * @param keyName Key name. 145 * @param lockMode The lock mode to use when performing the query. 146 * @param lookupType Specify whether this is a unique or non-unique query. 147 * @param direction Ordering for non-unique queries. 148 * @param keyFields Query key fields. 149 * @return A concept for a unique query or null, if not found. 150 * An iterator for a non-unique query. 151 */ 152 // TODO - Break into two separate methods - one for unique and one for non-unique 153 protected static Object lookupQuery( 154 final Class<?> klass, 155 final String keyName, 156 final String lockMode, 157 final LookupType lookupType, 158 final String direction, 159 final HashMap<String, Object> keyFields) 160 { 161 // 162 // define the query 163 // 164 KeyManager keyManager = new KeyManager(); 165 KeyQuery<?> keyQuery = keyManager.createKeyQuery(klass, keyName); 166 KeyFieldValueList keyFieldValueList = new KeyFieldValueList(); 167 168 // 169 // fill in the key fields 170 // 171 Set<String> fieldNames = keyFields.keySet(); 172 for (String fieldName : fieldNames) 173 { 174 keyFieldValueList.add(fieldName, keyFields.get(fieldName)); 175 } 176 keyQuery.defineQuery(keyFieldValueList); 177 178 if (TransactionService.isLoggerEnabledFor(Level.TRACE) == true) 179 { 180 TransactionService.log(Level.TRACE, "Query (" + mapLockMode(lockMode) + "): " 181 + keyQuery.toString()); 182 } 183 184 // 185 // if this is a non-unique query, just return the iterator. 186 // 187 if (lookupType == LookupType.NONUNIQUE_LOOKUP) 188 { 189 if (direction != null) 190 { 191 return keyQuery.getResults(mapOrderBy(direction), mapLockMode(lockMode)).iterator(); 192 } 193 return keyQuery.getResults(mapLockMode(lockMode)).iterator(); 194 } 195 196 assert lookupType == LookupType.UNIQUE_LOOKUP : klass; 197 198 Concept concept = (Concept)keyQuery.getSingleResult(mapLockMode(lockMode)); 199 200 if (concept == null) 201 { 202 return null; 203 } 204 205 // 206 // Load the concept into working memory 207 // 208 concept.load(); 209 210 return concept; 211 } 212 213 /** 214 * Determine the cardinality of the specified query 215 * @param klass Concept class 216 * @param keyName Key name 217 * @param keyFields Key fields 218 * @return The cardinality for the specified key and values. 219 */ 220 protected static Integer cardinality( 221 final Class<?> klass, 222 final String keyName, 223 final HashMap<String, Object> keyFields) 224 { 225 // 226 // define the query 227 // 228 KeyManager keyManager = new KeyManager(); 229 KeyQuery keyQuery = keyManager.createKeyQuery((Class<?>) klass, keyName); 230 KeyFieldValueList keyFieldValueList = new KeyFieldValueList(); 231 232 // 233 // fill in the key fields 234 // 235 Set<String> fieldNames = keyFields.keySet(); 236 for (String fieldName : fieldNames) 237 { 238 keyFieldValueList.add(fieldName, keyFields.get(fieldName)); 239 } 240 keyQuery.defineQuery(keyFieldValueList); 241 242 if (TransactionService.isLoggerEnabledFor(Level.TRACE) == true) 243 { 244 TransactionService.log(Level.TRACE, "Cardinality: " + keyQuery.toString()); 245 } 246 247 return keyQuery.cardinality(); 248 } 249 250 /** 251 * Map the lock mode value 252 * @param lockMode User provided lock mode value 253 * @return Mapped lock mode 254 * <p> 255 * A user provided lockMode value of read is mapped to a READLOCK. 256 * <p> 257 * A user provided lockMode value of write is mapped to WRITELOCK 258 * <p> 259 * All other values are mapped to NOLOCK. 260 */ 261 // TODO - This should not be public 262 public static LockMode mapLockMode(final String lockMode) 263 { 264 LockMode mappedValue = LockMode.NOLOCK; 265 266 if (lockMode.equals("read") == true) 267 { 268 mappedValue = LockMode.READLOCK; 269 } 270 271 if (lockMode.equals("write") == true) 272 { 273 mappedValue = LockMode.WRITELOCK; 274 } 275 276 return mappedValue; 277 } 278 279 280 /** 281 * Map the order-by direction 282 * @param direction User provided order-by direction 283 * @return Mapped order-by direction 284 * <p> 285 * A user provided direction value of descending is mapped to a DESCENDING. 286 * <p> 287 * All other values are mapped to ASCENDING. 288 */ 289 protected static KeyOrderedBy mapOrderBy(final String direction) 290 { 291 KeyOrderedBy mappedValue = KeyOrderedBy.ASCENDING; 292 293 if (direction.equals("descending") == true) 294 { 295 mappedValue = KeyOrderedBy.DESCENDING; 296 } 297 298 return mappedValue; 299 } 300 301 /** 302 * Refresh the concept 303 * <p> 304 * Concept values are refreshed from shared memory. 305 * @param original Concept to refresh. 306 * @return Refreshed concept. May be null. 307 */ 308 // TODO - Currently required to be public because it is used in the 309 // wrapper catalog function stubs 310 public static com.tibco.cep.runtime.model.element.Concept 311 refresh(com.tibco.cep.runtime.model.element.Concept original) 312 { 313 if (original == null) 314 { 315 return null; 316 } 317 318 assert original instanceof ConceptImpl == true : original; 319 320 // 321 // Fetch a current view of this entity from shared memory 322 // 323 ConceptImpl current = (ConceptImpl)TransactionService.getInstance().fetchById( 324 original.getId(), 325 original.getClass(), 326 ManagedObjectLockType.READLOCK); 327 328 // 329 // Nothing do to if concept doesn't exist in shared memory - it was deleted 330 // directly in Java code 331 // 332 if (current == null) 333 { 334 return null; 335 } 336 337 assert original.getClass().getName().equals(current.getClass().getName()) == true : original; 338 assert original.getProperties().length == current.getProperties().length : original; 339 340 // 341 // Update the original handle with the current view from shared memory 342 // 343 for (Property p : current.getProperties()) 344 { 345 // 346 // Refresh any references (or contained) concepts 347 // 348 if (p instanceof PropertyAtomConcept) 349 { 350 PropertyAtomConcept to = (PropertyAtomConcept)original.getPropertyAtom(p.getName()); 351 assert to != null : p.getName(); 352 353 if (to.getValue() == null) 354 { 355 356 to.setValue(((PropertyAtomConcept) p).getValue()); 357 continue; 358 } 359 refresh((com.tibco.cep.runtime.model.element.Concept)to.getValue()); 360 } 361 // 362 // Refresh arrays containing concepts 363 // 364 else if ((p instanceof PropertyArrayConceptReferenceSimple) 365 || (p instanceof PropertyArrayContainedConceptSimple)) 366 { 367 PropertyArray from = (PropertyArray)p; 368 PropertyArray to = original.getPropertyArray(p.getName()); 369 assert to != null : p.getName(); 370 PropertyAtom [ ] atoms = from.toArray(); 371 372 if ((atoms == null) || (atoms.length == 0)) 373 { 374 copyArray(from, to); 375 continue; 376 } 377 com.tibco.cep.runtime.model.element.Concept [ ] concepts 378 = new com.tibco.cep.runtime.model.element.Concept[atoms.length]; 379 for (int i = 0; i < atoms.length; i++) 380 { 381 if (atoms[i].getValue() == null) 382 { 383 continue; 384 } 385 assert atoms[i].getValue() instanceof ConceptImpl : atoms[i].getValue(); 386 ConceptImpl c = (ConceptImpl)atoms[i].getValue(); 387 388 // 389 // Clear parent reference. It will be reset when concept is refreshed 390 // 391 c.setParentReference(null); 392 concepts[i] = refresh(c); 393 } 394 copyArray(from, to); 395 } 396 // 397 // Primitive types 398 // 399 else if (p instanceof PropertyAtom) 400 { 401 try 402 { 403 PropertyAtom from = (PropertyAtom)p; 404 PropertyAtom to = original.getPropertyAtom(p.getName()); 405 assert to != null : p.getName(); 406 407 if (from.getValue() == null && to.getValue() == null) 408 { 409 continue; 410 } 411 if (((from.getValue() == null) && (to.getValue() != null)) 412 || (from.getValue().equals(to.getValue()) == false)) 413 { 414 to.setValue(from.getValue()); 415 } 416 } 417 catch (Exception e) 418 { 419 e.printStackTrace(); 420 } 421 } 422 // 423 // Arrays of primitive types 424 // 425 else 426 { 427 assert p instanceof PropertyArray : p.getName(); 428 PropertyArray from = (PropertyArray)p; 429 PropertyArray to = original.getPropertyArray(p.getName()); 430 assert to != null : p.getName(); 431 432 copyArray(from, to); 433 } 434 } 435 return original; 436 } 437 438 /** 439 * Refresh an array of concepts 440 * <p> 441 * All concepts in the array are refreshed from shared memory 442 * @param concepts Concept array to refresh 443 */ 444 // TODO - Currently required to be public because it is used in the 445 // wrapper catalog function stubs 446 public static void refreshArray( 447 com.tibco.cep.runtime.model.element.Concept [ ] concepts) 448 { 449 if (concepts == null) 450 { 451 return; 452 } 453 454 for (com.tibco.cep.runtime.model.element.Concept c : concepts) 455 { 456 refresh(c); 457 } 458 } 459 460 /** 461 * Update a concept in shared memory 462 * <p> 463 * Flush concepts to shared memory 464 * @param concept Concept to flush to shared memory. 465 */ 466 // TODO - Currently required to be public because it is used in the 467 // wrapper catalog function stubs 468 public static void update(com.tibco.cep.runtime.model.element.Concept concept) 469 { 470 if (concept == null) 471 { 472 return; 473 } 474 475 if (TransactionService.isLoggerEnabledFor(Level.TRACE) == true) 476 { 477 TransactionService.log(Level.TRACE, "Updating concept " + concept); 478 } 479 480 assert concept instanceof ConceptImpl : concept; 481 ConceptImpl conceptImpl = (ConceptImpl)concept; 482 Entity me = Entity.lookupById(conceptImpl.getId()); 483 484 // 485 // If we didn't find the entity we need to insert it. This can happen 486 // if an entity is created in an RTC and then a catalog function is called 487 // 488 if (me == null) 489 { 490 TransactionService.getInstance().insert(conceptImpl); 491 } 492 else 493 { 494 TransactionService.getInstance().update(conceptImpl); 495 } 496 497 // 498 // Recursively update any concept properties 499 // 500 for (Property p : concept.getProperties()) 501 { 502 if (p instanceof PropertyAtomConcept) 503 { 504 PropertyAtomConcept c = (PropertyAtomConcept)p; 505 if (c.isSet() == false) 506 { 507 continue; 508 } 509 update((com.tibco.cep.runtime.model.element.Concept)c.getValue()); 510 } 511 } 512 } 513 514 /** 515 * Update an array of concepts 516 * <p> 517 * All concepts in the array are updated in shared memory 518 * if they have been modified in the current RTC. 519 * @param concepts Concept array to update 520 */ 521 // TODO - Currently required to be public because it is used in the 522 // wrapper catalog function stubs 523 public static void updateArray( 524 com.tibco.cep.runtime.model.element.Concept [ ] concepts) 525 { 526 if (concepts == null) 527 { 528 return; 529 } 530 531 for (com.tibco.cep.runtime.model.element.Concept c : concepts) 532 { 533 update(c); 534 } 535 } 536 537 /** 538 * Set the parent identifier for a contained type 539 * @param parent Parent identifier 540 */ 541 protected final void setParent(long parent) 542 { 543 m_parent = parent; 544 } 545 546 /** 547 * Get the parent identifier for a contained type 548 * @return Parent identifier; 549 */ 550 protected final long getParent() 551 { 552 return m_parent; 553 } 554 555 /** 556 * Map auto-box array to primitive array 557 * @param a Array to map 558 * @return Mapped array 559 */ 560 protected final long [ ] toPrimitiveArray(Long [ ] a) 561 { 562 if (a == null) 563 { 564 return null; 565 } 566 long [ ] n = new long[a.length]; 567 for (int i = 0; i < a.length; i++) 568 { 569 n[i] = a[i]; 570 } 571 return n; 572 } 573 574 /** 575 * Map auto-box array to primitive array 576 * @param a Array to map 577 * @return Mapped array 578 */ 579 protected final int [ ] toPrimitiveArray(Integer [ ] a) 580 { 581 if (a == null) 582 { 583 return null; 584 } 585 int [ ] n = new int[a.length]; 586 for (int i = 0; i < a.length; i++) 587 { 588 n[i] = a[i]; 589 } 590 return n; 591 } 592 593 /** 594 * Map auto-box array to primitive array 595 * @param a Array to map 596 * @return Mapped array 597 */ 598 protected final boolean [ ] toPrimitiveArray(Boolean [ ] a) 599 { 600 if (a == null) 601 { 602 return null; 603 } 604 boolean [ ] n = new boolean[a.length]; 605 for (int i = 0; i < a.length; i++) 606 { 607 n[i] = a[i]; 608 } 609 return n; 610 } 611 612 /** 613 * Map auto-box array to primitive array 614 * @param a Array to map 615 * @return Mapped array 616 */ 617 protected final double [ ] toPrimitiveArray(Double [ ] a) 618 { 619 if (a == null) 620 { 621 return null; 622 } 623 double [ ] n = new double[a.length]; 624 for (int i = 0; i < a.length; i++) 625 { 626 n[i] = a[i]; 627 } 628 return n; 629 } 630 631 /** 632 * Map auto-box array to primitive array 633 * @param a Array to map 634 * @return Mapped array 635 */ 636 protected final String [ ] toPrimitiveArray(String [ ] a) 637 { 638 return a; 639 } 640 641 /** 642 * Map auto-box array to primitive array 643 * @param a Array to map 644 * @return Mapped array 645 */ 646 protected final Date [ ] toPrimitiveArray(Date [ ] a) 647 { 648 return a; 649 } 650 651 // 652 // Copy an array 653 // 654 private static void copyArray(PropertyArray from, PropertyArray to) 655 { 656 // 657 // Grow the target array 658 // 659 if (from.length() > to.length()) 660 { 661 for (int i = to.length(); i < from.length(); i++) 662 { 663 to.add(i, from.get(i).getValue()); 664 } 665 } 666 667 // 668 // Shrink the target array 669 // 670 if (to.length() > from.length()) 671 { 672 int originalLength = to.length(); 673 for (int i = from.length(); i < originalLength; i++) 674 { 675 assert to.length() > 0 : to.length(); 676 to.remove(to.length() - 1); 677 } 678 } 679 680 assert from.length() == to.length() : 681 from.getName() + "(" + from.length() + "):" 682 + to.getName() + "(" + to.length() + ")"; 683 684 // 685 // Update changed values 686 // 687 for (int i = 0; i < from.length(); i++) 688 { 689 if (from.get(i).getValue().equals(to.get(i).getValue()) == false) 690 { 691 to.get(i).setValue(from.get(i).getValue()); 692 } 693 } 694 } 695 696 private long m_parent = IdentifierGenerator.RESERVED_FOR_EMPTY_INDICATOR; 697 private static final long serialVersionUID = 5L; 698}