/*- * See the file LICENSE for redistribution information. * * Copyright (c) 2002, 2015 Oracle and/or its affiliates. All rights reserved. * */ package com.sleepycat.persist.test; import static com.sleepycat.persist.model.DeleteAction.NULLIFY; import static com.sleepycat.persist.model.Relationship.ONE_TO_MANY; import static com.sleepycat.persist.model.Relationship.ONE_TO_ONE; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Locale; import org.junit.After; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; import com.sleepycat.db.DatabaseConfig; import com.sleepycat.db.DatabaseException; import com.sleepycat.db.SecondaryConfig; import com.sleepycat.db.SequenceConfig; import com.sleepycat.persist.EntityStore; import com.sleepycat.persist.PrimaryIndex; import com.sleepycat.persist.SecondaryIndex; import com.sleepycat.persist.StoreConfig; import com.sleepycat.persist.model.AnnotationModel; import com.sleepycat.persist.model.Entity; import com.sleepycat.persist.model.KeyField; import com.sleepycat.persist.model.Persistent; import com.sleepycat.persist.model.PersistentProxy; import com.sleepycat.persist.model.PrimaryKey; import com.sleepycat.persist.model.SecondaryKey; import com.sleepycat.util.test.TxnTestCase; /** * Negative tests. * * @author Mark Hayes */ @RunWith(Parameterized.class) public class NegativeTest extends TxnTestCase { @Parameters public static List<Object[]> genParams() { return getTxnParams(null, false); } public NegativeTest(String type){ initEnvConfig(); txnType = type; isTransactional = (txnType != TXN_NULL); customName = txnType; } private EntityStore store; private void open() throws DatabaseException { open(null); } private void open(Class<ProxyExtendsEntity> clsToRegister) throws DatabaseException { StoreConfig config = new StoreConfig(); config.setAllowCreate(envConfig.getAllowCreate()); config.setTransactional(envConfig.getTransactional()); if (clsToRegister != null) { AnnotationModel model = new AnnotationModel(); model.registerClass(clsToRegister); config.setModel(model); } store = new EntityStore(env, "test", config); } private void close() throws DatabaseException { store.close(); store = null; } @After public void tearDown() throws Exception { if (store != null) { try { store.close(); } catch (Throwable e) { System.out.println("tearDown: " + e); } store = null; } super.tearDown(); } @Test public void testBadKeyClass1() throws DatabaseException { open(); try { store.getPrimaryIndex(BadKeyClass1.class, UseBadKeyClass1.class); fail(); } catch (IllegalArgumentException expected) { assertTrue(expected.getMessage().indexOf("@KeyField") >= 0); } close(); } /** Missing @KeyField in composite key class. */ @Persistent static class BadKeyClass1 { private int f1; } @Entity static class UseBadKeyClass1 { @PrimaryKey private final BadKeyClass1 f1 = new BadKeyClass1(); @SecondaryKey(relate=ONE_TO_ONE) private final BadKeyClass1 f2 = new BadKeyClass1(); } @Test public void testBadSequenceKeys() throws DatabaseException { open(); try { store.getPrimaryIndex(Boolean.class, BadSequenceKeyEntity1.class); fail(); } catch (IllegalArgumentException expected) { assertTrue(expected.getMessage().indexOf ("Type not allowed for sequence") >= 0); } try { store.getPrimaryIndex(BadSequenceKeyEntity2.Key.class, BadSequenceKeyEntity2.class); fail(); } catch (IllegalArgumentException expected) { assertTrue(expected.getMessage().indexOf ("Type not allowed for sequence") >= 0); } try { store.getPrimaryIndex(BadSequenceKeyEntity3.Key.class, BadSequenceKeyEntity3.class); fail(); } catch (IllegalArgumentException expected) { assertTrue(expected.getMessage().indexOf ("A composite key class used with a sequence may contain " + "only a single key field")>= 0); } close(); } /** Boolean not allowed for sequence key. */ @Entity static class BadSequenceKeyEntity1 { @PrimaryKey(sequence="X") private boolean key; } /** Composite key with non-integer field not allowed for sequence key. */ @Entity static class BadSequenceKeyEntity2 { @PrimaryKey(sequence="X") private Key key; @Persistent static class Key { @KeyField(1) boolean key; } } /** Composite key with multiple key fields not allowed for sequence key. */ @Entity static class BadSequenceKeyEntity3 { @PrimaryKey(sequence="X") private Key key; @Persistent static class Key { @KeyField(1) int key; @KeyField(2) int key2; } } /** * A proxied object may not current contain a field that references the * parent proxy. [#15815] */ @Test public void testProxyNestedRef() throws DatabaseException { open(); PrimaryIndex<Integer, ProxyNestedRef> index = store.getPrimaryIndex (Integer.class, ProxyNestedRef.class); ProxyNestedRef entity = new ProxyNestedRef(); entity.list.add(entity.list); try { index.put(entity); fail(); } catch (IllegalArgumentException expected) { assertTrue(expected.getMessage().indexOf ("Cannot embed a reference to a proxied object") >= 0); } close(); } @Entity static class ProxyNestedRef { @PrimaryKey private int key; ArrayList<Object> list = new ArrayList<Object>(); } /** * Disallow primary keys on entity subclasses. [#15757] */ @Test public void testEntitySubclassWithPrimaryKey() throws DatabaseException { open(); PrimaryIndex<Integer, EntitySuperClass> index = store.getPrimaryIndex (Integer.class, EntitySuperClass.class); EntitySuperClass e1 = new EntitySuperClass(1, "one"); index.put(e1); assertEquals(e1, index.get(1)); EntitySubClass e2 = new EntitySubClass(2, "two", "foo", 9); try { index.put(e2); fail(); } catch (IllegalArgumentException e) { assertTrue(e.getMessage().contains ("PrimaryKey may not appear on an Entity subclass")); } assertEquals(e1, index.get(1)); close(); } @Entity static class EntitySuperClass { @PrimaryKey private int x; private String y; EntitySuperClass(int x, String y) { assert y != null; this.x = x; this.y = y; } private EntitySuperClass() {} @Override public String toString() { return "x=" + x + " y=" + y; } @Override public boolean equals(Object other) { if (other instanceof EntitySuperClass) { EntitySuperClass o = (EntitySuperClass) other; return x == o.x && y.equals(o.y); } else { return false; } } } @Persistent static class EntitySubClass extends EntitySuperClass { @PrimaryKey private String foo; private int z; EntitySubClass(int x, String y, String foo, int z) { super(x, y); assert foo != null; this.foo = foo; this.z = z; } private EntitySubClass() {} @Override public String toString() { return super.toString() + " z=" + z; } @Override public boolean equals(Object other) { if (other instanceof EntitySubClass) { EntitySubClass o = (EntitySubClass) other; return super.equals(o) && z == o.z; } else { return false; } } } /** * Disallow storing null entities. [#19085] */ @Test public void testNullEntity() throws DatabaseException { open(); PrimaryIndex<Integer, EntitySuperClass> index = store.getPrimaryIndex (Integer.class, EntitySuperClass.class); try { index.put(null); fail(); } catch (IllegalArgumentException expected) { } try { index.sortedMap().put(1, null); fail(); } catch (IllegalArgumentException expected) { } close(); } /** * Disallow embedded entity classes and subclasses. [#16077] */ @Test public void testEmbeddedEntity() throws DatabaseException { open(); PrimaryIndex<Integer, EmbeddingEntity> index = store.getPrimaryIndex (Integer.class, EmbeddingEntity.class); EmbeddingEntity e1 = new EmbeddingEntity(1, null); index.put(e1); assertEquals(e1, index.get(1)); EmbeddingEntity e2 = new EmbeddingEntity(2, new EntitySuperClass(2, "two")); try { index.put(e2); fail(); } catch (IllegalArgumentException e) { assertTrue(e.getMessage().contains ("References to entities are not allowed")); } EmbeddingEntity e3 = new EmbeddingEntity (3, new EmbeddedEntitySubClass(3, "three", "foo", 9)); try { index.put(e3); fail(); } catch (IllegalArgumentException e) { assertTrue(e.toString(), e.getMessage().contains ("References to entities are not allowed")); } assertEquals(e1, index.get(1)); close(); } @Entity static class EmbeddingEntity { @PrimaryKey private int x; private EntitySuperClass y; /* References to self are allowed. [#17525] */ private EmbeddingEntity self; EmbeddingEntity(int x, EntitySuperClass y) { this.x = x; this.y = y; this.self = this; } private EmbeddingEntity() {} @Override public String toString() { return "x=" + x + " y=" + y; } @Override public boolean equals(Object other) { if (other instanceof EmbeddingEntity) { EmbeddingEntity o = (EmbeddingEntity) other; return x == o.x && ((y == null) ? (o.y == null) : y.equals(o.y)); } else { return false; } } } @Persistent static class EmbeddedEntitySubClass extends EntitySuperClass { private String foo; private int z; EmbeddedEntitySubClass(int x, String y, String foo, int z) { super(x, y); assert foo != null; this.foo = foo; this.z = z; } private EmbeddedEntitySubClass() {} @Override public String toString() { return super.toString() + " z=" + z; } @Override public boolean equals(Object other) { if (other instanceof EmbeddedEntitySubClass) { EmbeddedEntitySubClass o = (EmbeddedEntitySubClass) other; return super.equals(o) && z == o.z; } else { return false; } } } /** * Disallow SecondaryKey collection with no type parameter. [#15950] */ @Test public void testTypelessKeyCollection() throws DatabaseException { open(); try { store.getPrimaryIndex (Integer.class, TypelessKeyCollectionEntity.class); fail(); } catch (IllegalArgumentException e) { assertTrue(e.toString(), e.getMessage().contains ("Collection typed secondary key field must have a " + "single generic type argument and a wildcard or type " + "bound is not allowed")); } close(); } @Entity static class TypelessKeyCollectionEntity { @PrimaryKey private int x; @SecondaryKey(relate=ONE_TO_MANY) private final Collection keys = new ArrayList(); TypelessKeyCollectionEntity(int x) { this.x = x; } private TypelessKeyCollectionEntity() {} } /** * Disallow a persistent proxy that extends an entity. [#15950] */ @Test public void testProxyEntity() throws DatabaseException { try { open(ProxyExtendsEntity.class); fail(); } catch (IllegalArgumentException e) { assertTrue(e.toString(), e.getMessage().contains ("A proxy may not be an entity")); } } @Persistent(proxyFor=Locale.class) static class ProxyExtendsEntity extends EntitySuperClass implements PersistentProxy<Locale> { String language; String country; String variant; public void initializeProxy(Locale object) { language = object.getLanguage(); country = object.getCountry(); variant = object.getVariant(); } public Locale convertProxy() { return new Locale(language, country, variant); } } /** * Wrapper type not allowed for nullified foreign key. */ @Test public void testBadNullifyKey() throws DatabaseException { open(); try { store.getPrimaryIndex(Integer.class, BadNullifyKeyEntity1.class); fail(); } catch (IllegalArgumentException expected) { assertTrue(expected.getMessage().indexOf ("NULLIFY may not be used with primitive fields") >= 0); } close(); } @Entity static class BadNullifyKeyEntity1 { @PrimaryKey private int key; @SecondaryKey(relate=ONE_TO_ONE, relatedEntity=BadNullifyKeyEntity2.class, onRelatedEntityDelete=NULLIFY) private int secKey; // Should be Integer, not int. } @Entity static class BadNullifyKeyEntity2 { @PrimaryKey private int key; } /** * @Persistent not allowed on an enum. */ @Test public void testPersistentEnum() throws DatabaseException { open(); try { store.getPrimaryIndex(Integer.class, PersistentEnumEntity.class); fail(); } catch (IllegalArgumentException expected) { assertTrue(expected.getMessage().indexOf ("not allowed for enum, interface, or primitive") >= 0); } close(); } @Entity static class PersistentEnumEntity { @PrimaryKey private int key; @Persistent enum MyEnum {X, Y, Z}; MyEnum f1; } /** * Disallow a reference to an interface marked @Persistent. */ @Test public void testPersistentInterface() throws DatabaseException { open(); try { store.getPrimaryIndex(Integer.class, PersistentInterfaceEntity1.class); fail(); } catch (IllegalArgumentException expected) { assertTrue(expected.getMessage().indexOf ("not allowed for enum, interface, or primitive") >= 0); } close(); } @Entity static class PersistentInterfaceEntity1 { @PrimaryKey private int key; @SecondaryKey(relate=ONE_TO_ONE, relatedEntity=PersistentInterfaceEntity2.class) private int secKey; // Should be Integer, not int. } @Persistent interface PersistentInterfaceEntity2 { } /** * Disallow reference to @Persistent inner class. */ @Test public void testPersistentInnerClass() throws DatabaseException { open(); try { store.getPrimaryIndex(Integer.class, PersistentInnerClassEntity1.class); fail(); } catch (IllegalArgumentException expected) { assertTrue(expected.getMessage().indexOf ("Inner classes not allowed") >= 0); } close(); } @Entity static class PersistentInnerClassEntity1 { @PrimaryKey private int key; private PersistentInnerClass f; } /* An inner (non-static) class is illegal. */ @Persistent class PersistentInnerClass { private int x; } /** * Disallow @Entity inner class. */ @Test public void testSetConfigAfterOpen() throws DatabaseException { open(); PrimaryIndex<Integer, SetConfigAfterOpenEntity> priIndex = store.getPrimaryIndex(Integer.class, SetConfigAfterOpenEntity.class); SecondaryIndex<Integer, Integer, SetConfigAfterOpenEntity> secIndex = store.getSecondaryIndex(priIndex, Integer.class, "skey"); DatabaseConfig priConfig = store.getPrimaryConfig(SetConfigAfterOpenEntity.class); assertNotNull(priConfig); try { store.setPrimaryConfig(SetConfigAfterOpenEntity.class, priConfig); fail(); } catch (IllegalStateException expected) { assertTrue(expected.getMessage().indexOf ("Cannot set config after DB is open") >= 0); } SecondaryConfig secConfig = store.getSecondaryConfig(SetConfigAfterOpenEntity.class, "skey"); assertNotNull(secConfig); try { store.setSecondaryConfig(SetConfigAfterOpenEntity.class, "skey", secConfig); fail(); } catch (IllegalStateException expected) { assertTrue(expected.getMessage().indexOf ("Cannot set config after DB is open") >= 0); } SequenceConfig seqConfig = store.getSequenceConfig("foo"); assertNotNull(seqConfig); try { store.setSequenceConfig("foo", seqConfig); fail(); } catch (IllegalStateException expected) { assertTrue(expected.getMessage().indexOf ("Cannot set config after Sequence is open") >= 0); } close(); } @Entity static class SetConfigAfterOpenEntity { @PrimaryKey(sequence="foo") private int key; @SecondaryKey(relate=ONE_TO_ONE) int skey; } }