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