1 /*- 2 * Copyright (c) 2002, 2020 Oracle and/or its affiliates. All rights reserved. 3 * 4 * See the file LICENSE for license information. 5 * 6 */ 7 8 package com.sleepycat.persist.test; 9 10 import static com.sleepycat.persist.model.DeleteAction.NULLIFY; 11 import static com.sleepycat.persist.model.Relationship.ONE_TO_MANY; 12 import static com.sleepycat.persist.model.Relationship.ONE_TO_ONE; 13 import static org.junit.Assert.assertEquals; 14 import static org.junit.Assert.assertNotNull; 15 import static org.junit.Assert.assertTrue; 16 import static org.junit.Assert.fail; 17 18 import java.util.ArrayList; 19 import java.util.Collection; 20 import java.util.List; 21 import java.util.Locale; 22 23 import org.junit.After; 24 import org.junit.Test; 25 import org.junit.runner.RunWith; 26 import org.junit.runners.Parameterized; 27 import org.junit.runners.Parameterized.Parameters; 28 29 import com.sleepycat.db.DatabaseConfig; 30 import com.sleepycat.db.DatabaseException; 31 import com.sleepycat.db.SecondaryConfig; 32 import com.sleepycat.db.SequenceConfig; 33 import com.sleepycat.persist.EntityStore; 34 import com.sleepycat.persist.PrimaryIndex; 35 import com.sleepycat.persist.SecondaryIndex; 36 import com.sleepycat.persist.StoreConfig; 37 import com.sleepycat.persist.model.AnnotationModel; 38 import com.sleepycat.persist.model.Entity; 39 import com.sleepycat.persist.model.KeyField; 40 import com.sleepycat.persist.model.Persistent; 41 import com.sleepycat.persist.model.PersistentProxy; 42 import com.sleepycat.persist.model.PrimaryKey; 43 import com.sleepycat.persist.model.SecondaryKey; 44 import com.sleepycat.util.test.TxnTestCase; 45 46 /** 47 * Negative tests. 48 * 49 * @author Mark Hayes 50 */ 51 @RunWith(Parameterized.class) 52 public class NegativeTest extends TxnTestCase { 53 54 @Parameters genParams()55 public static List<Object[]> genParams() { 56 return getTxnParams(null, false); 57 } 58 NegativeTest(String type)59 public NegativeTest(String type){ 60 initEnvConfig(); 61 txnType = type; 62 isTransactional = (txnType != TXN_NULL); 63 customName = txnType; 64 } 65 66 private EntityStore store; 67 open()68 private void open() 69 throws DatabaseException { 70 71 open(null); 72 } 73 open(Class<ProxyExtendsEntity> clsToRegister)74 private void open(Class<ProxyExtendsEntity> clsToRegister) 75 throws DatabaseException { 76 77 StoreConfig config = new StoreConfig(); 78 config.setAllowCreate(envConfig.getAllowCreate()); 79 config.setTransactional(envConfig.getTransactional()); 80 81 if (clsToRegister != null) { 82 AnnotationModel model = new AnnotationModel(); 83 model.registerClass(clsToRegister); 84 config.setModel(model); 85 } 86 87 store = new EntityStore(env, "test", config); 88 } 89 close()90 private void close() 91 throws DatabaseException { 92 93 store.close(); 94 store = null; 95 } 96 97 @After tearDown()98 public void tearDown() 99 throws Exception { 100 101 if (store != null) { 102 try { 103 store.close(); 104 } catch (Throwable e) { 105 System.out.println("tearDown: " + e); 106 } 107 store = null; 108 } 109 super.tearDown(); 110 } 111 112 @Test testBadKeyClass1()113 public void testBadKeyClass1() 114 throws DatabaseException { 115 116 open(); 117 try { 118 store.getPrimaryIndex(BadKeyClass1.class, UseBadKeyClass1.class); 119 fail(); 120 } catch (IllegalArgumentException expected) { 121 assertTrue(expected.getMessage().indexOf("@KeyField") >= 0); 122 } 123 close(); 124 } 125 126 /** Missing @KeyField in composite key class. */ 127 @Persistent 128 static class BadKeyClass1 { 129 130 private int f1; 131 } 132 133 @Entity 134 static class UseBadKeyClass1 { 135 136 @PrimaryKey 137 private final BadKeyClass1 f1 = new BadKeyClass1(); 138 139 @SecondaryKey(relate=ONE_TO_ONE) 140 private final BadKeyClass1 f2 = new BadKeyClass1(); 141 } 142 143 @Test testBadSequenceKeys()144 public void testBadSequenceKeys() 145 throws DatabaseException { 146 147 open(); 148 try { 149 store.getPrimaryIndex(Boolean.class, BadSequenceKeyEntity1.class); 150 fail(); 151 } catch (IllegalArgumentException expected) { 152 assertTrue(expected.getMessage().indexOf 153 ("Type not allowed for sequence") >= 0); 154 } 155 try { 156 store.getPrimaryIndex(BadSequenceKeyEntity2.Key.class, 157 BadSequenceKeyEntity2.class); 158 fail(); 159 } catch (IllegalArgumentException expected) { 160 assertTrue(expected.getMessage().indexOf 161 ("Type not allowed for sequence") >= 0); 162 } 163 try { 164 store.getPrimaryIndex(BadSequenceKeyEntity3.Key.class, 165 BadSequenceKeyEntity3.class); 166 fail(); 167 } catch (IllegalArgumentException expected) { 168 assertTrue(expected.getMessage().indexOf 169 ("A composite key class used with a sequence may contain " + 170 "only a single key field")>= 0); 171 } 172 close(); 173 } 174 175 /** Boolean not allowed for sequence key. */ 176 @Entity 177 static class BadSequenceKeyEntity1 { 178 179 @PrimaryKey(sequence="X") 180 private boolean key; 181 } 182 183 /** Composite key with non-integer field not allowed for sequence key. */ 184 @Entity 185 static class BadSequenceKeyEntity2 { 186 187 @PrimaryKey(sequence="X") 188 private Key key; 189 190 @Persistent 191 static class Key { 192 @KeyField(1) 193 boolean key; 194 } 195 } 196 197 /** Composite key with multiple key fields not allowed for sequence key. */ 198 @Entity 199 static class BadSequenceKeyEntity3 { 200 201 @PrimaryKey(sequence="X") 202 private Key key; 203 204 @Persistent 205 static class Key { 206 @KeyField(1) 207 int key; 208 @KeyField(2) 209 int key2; 210 } 211 } 212 213 /** 214 * A proxied object may not current contain a field that references the 215 * parent proxy. [#15815] 216 */ 217 @Test testProxyNestedRef()218 public void testProxyNestedRef() 219 throws DatabaseException { 220 221 open(); 222 PrimaryIndex<Integer, ProxyNestedRef> index = store.getPrimaryIndex 223 (Integer.class, ProxyNestedRef.class); 224 ProxyNestedRef entity = new ProxyNestedRef(); 225 entity.list.add(entity.list); 226 try { 227 index.put(entity); 228 fail(); 229 } catch (IllegalArgumentException expected) { 230 assertTrue(expected.getMessage().indexOf 231 ("Cannot embed a reference to a proxied object") >= 0); 232 } 233 close(); 234 } 235 236 @Entity 237 static class ProxyNestedRef { 238 239 @PrimaryKey 240 private int key; 241 242 ArrayList<Object> list = new ArrayList<Object>(); 243 } 244 245 /** 246 * Disallow primary keys on entity subclasses. [#15757] 247 */ 248 @Test testEntitySubclassWithPrimaryKey()249 public void testEntitySubclassWithPrimaryKey() 250 throws DatabaseException { 251 252 open(); 253 PrimaryIndex<Integer, EntitySuperClass> index = store.getPrimaryIndex 254 (Integer.class, EntitySuperClass.class); 255 EntitySuperClass e1 = new EntitySuperClass(1, "one"); 256 index.put(e1); 257 assertEquals(e1, index.get(1)); 258 EntitySubClass e2 = new EntitySubClass(2, "two", "foo", 9); 259 try { 260 index.put(e2); 261 fail(); 262 } catch (IllegalArgumentException e) { 263 assertTrue(e.getMessage().contains 264 ("PrimaryKey may not appear on an Entity subclass")); 265 } 266 assertEquals(e1, index.get(1)); 267 close(); 268 } 269 270 @Entity 271 static class EntitySuperClass { 272 273 @PrimaryKey 274 private int x; 275 276 private String y; 277 EntitySuperClass(int x, String y)278 EntitySuperClass(int x, String y) { 279 assert y != null; 280 this.x = x; 281 this.y = y; 282 } 283 EntitySuperClass()284 private EntitySuperClass() {} 285 286 @Override toString()287 public String toString() { 288 return "x=" + x + " y=" + y; 289 } 290 291 @Override equals(Object other)292 public boolean equals(Object other) { 293 if (other instanceof EntitySuperClass) { 294 EntitySuperClass o = (EntitySuperClass) other; 295 return x == o.x && y.equals(o.y); 296 } else { 297 return false; 298 } 299 } 300 } 301 302 @Persistent 303 static class EntitySubClass extends EntitySuperClass { 304 305 @PrimaryKey 306 private String foo; 307 308 private int z; 309 EntitySubClass(int x, String y, String foo, int z)310 EntitySubClass(int x, String y, String foo, int z) { 311 super(x, y); 312 assert foo != null; 313 this.foo = foo; 314 this.z = z; 315 } 316 EntitySubClass()317 private EntitySubClass() {} 318 319 @Override toString()320 public String toString() { 321 return super.toString() + " z=" + z; 322 } 323 324 @Override equals(Object other)325 public boolean equals(Object other) { 326 if (other instanceof EntitySubClass) { 327 EntitySubClass o = (EntitySubClass) other; 328 return super.equals(o) && z == o.z; 329 } else { 330 return false; 331 } 332 } 333 } 334 335 /** 336 * Disallow storing null entities. [#19085] 337 */ 338 @Test testNullEntity()339 public void testNullEntity() 340 throws DatabaseException { 341 342 open(); 343 PrimaryIndex<Integer, EntitySuperClass> index = store.getPrimaryIndex 344 (Integer.class, EntitySuperClass.class); 345 try { 346 index.put(null); 347 fail(); 348 } catch (IllegalArgumentException expected) { 349 } 350 try { 351 index.sortedMap().put(1, null); 352 fail(); 353 } catch (IllegalArgumentException expected) { 354 } 355 close(); 356 } 357 358 /** 359 * Disallow embedded entity classes and subclasses. [#16077] 360 */ 361 @Test testEmbeddedEntity()362 public void testEmbeddedEntity() 363 throws DatabaseException { 364 365 open(); 366 PrimaryIndex<Integer, EmbeddingEntity> index = store.getPrimaryIndex 367 (Integer.class, EmbeddingEntity.class); 368 EmbeddingEntity e1 = new EmbeddingEntity(1, null); 369 index.put(e1); 370 assertEquals(e1, index.get(1)); 371 372 EmbeddingEntity e2 = 373 new EmbeddingEntity(2, new EntitySuperClass(2, "two")); 374 try { 375 index.put(e2); 376 fail(); 377 } catch (IllegalArgumentException e) { 378 assertTrue(e.getMessage().contains 379 ("References to entities are not allowed")); 380 } 381 382 EmbeddingEntity e3 = new EmbeddingEntity 383 (3, new EmbeddedEntitySubClass(3, "three", "foo", 9)); 384 try { 385 index.put(e3); 386 fail(); 387 } catch (IllegalArgumentException e) { 388 assertTrue(e.toString(), e.getMessage().contains 389 ("References to entities are not allowed")); 390 } 391 392 assertEquals(e1, index.get(1)); 393 close(); 394 } 395 396 @Entity 397 static class EmbeddingEntity { 398 399 @PrimaryKey 400 private int x; 401 402 private EntitySuperClass y; 403 404 /* References to self are allowed. [#17525] */ 405 private EmbeddingEntity self; 406 EmbeddingEntity(int x, EntitySuperClass y)407 EmbeddingEntity(int x, EntitySuperClass y) { 408 this.x = x; 409 this.y = y; 410 this.self = this; 411 } 412 EmbeddingEntity()413 private EmbeddingEntity() {} 414 415 @Override toString()416 public String toString() { 417 return "x=" + x + " y=" + y; 418 } 419 420 @Override equals(Object other)421 public boolean equals(Object other) { 422 if (other instanceof EmbeddingEntity) { 423 EmbeddingEntity o = (EmbeddingEntity) other; 424 return x == o.x && 425 ((y == null) ? (o.y == null) : y.equals(o.y)); 426 } else { 427 return false; 428 } 429 } 430 } 431 432 @Persistent 433 static class EmbeddedEntitySubClass extends EntitySuperClass { 434 435 private String foo; 436 437 private int z; 438 EmbeddedEntitySubClass(int x, String y, String foo, int z)439 EmbeddedEntitySubClass(int x, String y, String foo, int z) { 440 super(x, y); 441 assert foo != null; 442 this.foo = foo; 443 this.z = z; 444 } 445 EmbeddedEntitySubClass()446 private EmbeddedEntitySubClass() {} 447 448 @Override toString()449 public String toString() { 450 return super.toString() + " z=" + z; 451 } 452 453 @Override equals(Object other)454 public boolean equals(Object other) { 455 if (other instanceof EmbeddedEntitySubClass) { 456 EmbeddedEntitySubClass o = (EmbeddedEntitySubClass) other; 457 return super.equals(o) && z == o.z; 458 } else { 459 return false; 460 } 461 } 462 } 463 464 /** 465 * Disallow SecondaryKey collection with no type parameter. [#15950] 466 */ 467 @Test testTypelessKeyCollection()468 public void testTypelessKeyCollection() 469 throws DatabaseException { 470 471 open(); 472 try { 473 store.getPrimaryIndex 474 (Integer.class, TypelessKeyCollectionEntity.class); 475 fail(); 476 } catch (IllegalArgumentException e) { 477 assertTrue(e.toString(), e.getMessage().contains 478 ("Collection typed secondary key field must have a " + 479 "single generic type argument and a wildcard or type " + 480 "bound is not allowed")); 481 } 482 close(); 483 } 484 485 @Entity 486 static class TypelessKeyCollectionEntity { 487 488 @PrimaryKey 489 private int x; 490 491 @SecondaryKey(relate=ONE_TO_MANY) 492 private final Collection keys = new ArrayList(); 493 TypelessKeyCollectionEntity(int x)494 TypelessKeyCollectionEntity(int x) { 495 this.x = x; 496 } 497 TypelessKeyCollectionEntity()498 private TypelessKeyCollectionEntity() {} 499 } 500 501 /** 502 * Disallow a persistent proxy that extends an entity. [#15950] 503 */ 504 @Test testProxyEntity()505 public void testProxyEntity() 506 throws DatabaseException { 507 508 try { 509 open(ProxyExtendsEntity.class); 510 fail(); 511 } catch (IllegalArgumentException e) { 512 assertTrue(e.toString(), e.getMessage().contains 513 ("A proxy may not be an entity")); 514 } 515 } 516 517 @Persistent(proxyFor=Locale.class) 518 static class ProxyExtendsEntity 519 extends EntitySuperClass 520 implements PersistentProxy<Locale> { 521 522 String language; 523 String country; 524 String variant; 525 initializeProxy(Locale object)526 public void initializeProxy(Locale object) { 527 language = object.getLanguage(); 528 country = object.getCountry(); 529 variant = object.getVariant(); 530 } 531 convertProxy()532 public Locale convertProxy() { 533 return new Locale(language, country, variant); 534 } 535 } 536 537 /** 538 * Wrapper type not allowed for nullified foreign key. 539 */ 540 @Test testBadNullifyKey()541 public void testBadNullifyKey() 542 throws DatabaseException { 543 544 open(); 545 try { 546 store.getPrimaryIndex(Integer.class, BadNullifyKeyEntity1.class); 547 fail(); 548 } catch (IllegalArgumentException expected) { 549 assertTrue(expected.getMessage().indexOf 550 ("NULLIFY may not be used with primitive fields") >= 0); 551 } 552 close(); 553 } 554 555 @Entity 556 static class BadNullifyKeyEntity1 { 557 558 @PrimaryKey 559 private int key; 560 561 @SecondaryKey(relate=ONE_TO_ONE, 562 relatedEntity=BadNullifyKeyEntity2.class, 563 onRelatedEntityDelete=NULLIFY) 564 private int secKey; // Should be Integer, not int. 565 } 566 567 @Entity 568 static class BadNullifyKeyEntity2 { 569 570 @PrimaryKey 571 private int key; 572 } 573 574 /** 575 * @Persistent not allowed on an enum. 576 */ 577 @Test testPersistentEnum()578 public void testPersistentEnum() 579 throws DatabaseException { 580 581 open(); 582 try { 583 store.getPrimaryIndex(Integer.class, PersistentEnumEntity.class); 584 fail(); 585 } catch (IllegalArgumentException expected) { 586 assertTrue(expected.getMessage().indexOf 587 ("not allowed for enum, interface, or primitive") >= 0); 588 } 589 close(); 590 } 591 592 @Entity 593 static class PersistentEnumEntity { 594 595 @PrimaryKey 596 private int key; 597 598 @Persistent 599 enum MyEnum {X, Y, Z}; 600 601 MyEnum f1; 602 } 603 604 /** 605 * Disallow a reference to an interface marked @Persistent. 606 */ 607 @Test testPersistentInterface()608 public void testPersistentInterface() 609 throws DatabaseException { 610 611 open(); 612 try { 613 store.getPrimaryIndex(Integer.class, 614 PersistentInterfaceEntity1.class); 615 fail(); 616 } catch (IllegalArgumentException expected) { 617 assertTrue(expected.getMessage().indexOf 618 ("not allowed for enum, interface, or primitive") >= 0); 619 } 620 close(); 621 } 622 623 @Entity 624 static class PersistentInterfaceEntity1 { 625 626 @PrimaryKey 627 private int key; 628 629 @SecondaryKey(relate=ONE_TO_ONE, 630 relatedEntity=PersistentInterfaceEntity2.class) 631 private int secKey; // Should be Integer, not int. 632 } 633 634 @Persistent 635 interface PersistentInterfaceEntity2 { 636 } 637 638 /** 639 * Disallow reference to @Persistent inner class. 640 */ 641 @Test testPersistentInnerClass()642 public void testPersistentInnerClass() 643 throws DatabaseException { 644 645 open(); 646 try { 647 store.getPrimaryIndex(Integer.class, 648 PersistentInnerClassEntity1.class); 649 fail(); 650 } catch (IllegalArgumentException expected) { 651 assertTrue(expected.getMessage().indexOf 652 ("Inner classes not allowed") >= 0); 653 } 654 close(); 655 } 656 657 @Entity 658 static class PersistentInnerClassEntity1 { 659 660 @PrimaryKey 661 private int key; 662 663 private PersistentInnerClass f; 664 } 665 666 /* An inner (non-static) class is illegal. */ 667 @Persistent 668 class PersistentInnerClass { 669 670 private int x; 671 } 672 673 /** 674 * Disallow @Entity inner class. 675 */ 676 @Test testSetConfigAfterOpen()677 public void testSetConfigAfterOpen() 678 throws DatabaseException { 679 680 open(); 681 PrimaryIndex<Integer, SetConfigAfterOpenEntity> priIndex = 682 store.getPrimaryIndex(Integer.class, 683 SetConfigAfterOpenEntity.class); 684 SecondaryIndex<Integer, Integer, SetConfigAfterOpenEntity> secIndex = 685 store.getSecondaryIndex(priIndex, Integer.class, "skey"); 686 687 DatabaseConfig priConfig = 688 store.getPrimaryConfig(SetConfigAfterOpenEntity.class); 689 assertNotNull(priConfig); 690 try { 691 store.setPrimaryConfig(SetConfigAfterOpenEntity.class, priConfig); 692 fail(); 693 } catch (IllegalStateException expected) { 694 assertTrue(expected.getMessage().indexOf 695 ("Cannot set config after DB is open") >= 0); 696 } 697 698 SecondaryConfig secConfig = 699 store.getSecondaryConfig(SetConfigAfterOpenEntity.class, "skey"); 700 assertNotNull(secConfig); 701 try { 702 store.setSecondaryConfig(SetConfigAfterOpenEntity.class, "skey", 703 secConfig); 704 fail(); 705 } catch (IllegalStateException expected) { 706 assertTrue(expected.getMessage().indexOf 707 ("Cannot set config after DB is open") >= 0); 708 } 709 710 SequenceConfig seqConfig = store.getSequenceConfig("foo"); 711 assertNotNull(seqConfig); 712 try { 713 store.setSequenceConfig("foo", seqConfig); 714 fail(); 715 } catch (IllegalStateException expected) { 716 assertTrue(expected.getMessage().indexOf 717 ("Cannot set config after Sequence is open") >= 0); 718 } 719 720 close(); 721 } 722 723 @Entity 724 static class SetConfigAfterOpenEntity { 725 726 @PrimaryKey(sequence="foo") 727 private int key; 728 729 @SecondaryKey(relate=ONE_TO_ONE) 730 int skey; 731 } 732 } 733