diff options
author | Eike Stepper | 2013-03-08 07:54:13 +0000 |
---|---|---|
committer | Eike Stepper | 2013-03-08 07:54:13 +0000 |
commit | ef293640557a8709debf62f11e2fad7841af984a (patch) | |
tree | 48e2b1e14c974655326cd87fb3ed581a920e5dde | |
parent | 549c7bf0b7c58f0cd49eab63149dd79654f1451e (diff) | |
download | cdo-ef293640557a8709debf62f11e2fad7841af984a.tar.gz cdo-ef293640557a8709debf62f11e2fad7841af984a.tar.xz cdo-ef293640557a8709debf62f11e2fad7841af984a.zip |
[401763] Make CDO Server more robust against data dictionary changes
https://bugs.eclipse.org/bugs/show_bug.cgi?id=401763
9 files changed, 319 insertions, 59 deletions
diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStore.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStore.java index 38245e4917..db1532f447 100644 --- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStore.java +++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStore.java @@ -46,7 +46,6 @@ import org.eclipse.net4j.db.IDBDatabase; import org.eclipse.net4j.db.ddl.IDBField; import org.eclipse.net4j.db.ddl.IDBSchema; import org.eclipse.net4j.db.ddl.IDBTable; -import org.eclipse.net4j.db.ddl.delta.IDBDelta; import org.eclipse.net4j.spi.db.DBSchema; import org.eclipse.net4j.util.ReflectUtil.ExcludeFromDump; import org.eclipse.net4j.util.lifecycle.LifecycleUtil; @@ -625,7 +624,7 @@ public class DBStore extends Store implements IDBStore, CDOAllRevisionsProvider IDBSchema schema = createSchema(); database = DBUtil.openDatabase(dbAdapter, dbConnectionProvider, repository.getName()); - database.ensureSchema(schema, IDBDelta.ChangeKind.ADD); + database.ensureSchema(schema); LifecycleUtil.activate(idHandler); LifecycleUtil.activate(metaDataManager); diff --git a/plugins/org.eclipse.net4j.db.tests/src/org/eclipse/net4j/db/tests/AbstractDBTest.java b/plugins/org.eclipse.net4j.db.tests/src/org/eclipse/net4j/db/tests/AbstractDBTest.java index 03c3403baa..0843467764 100644 --- a/plugins/org.eclipse.net4j.db.tests/src/org/eclipse/net4j/db/tests/AbstractDBTest.java +++ b/plugins/org.eclipse.net4j.db.tests/src/org/eclipse/net4j/db/tests/AbstractDBTest.java @@ -116,13 +116,13 @@ public abstract class AbstractDBTest extends AbstractOMTest assertEquals(count, tables.size()); } - public void testSchemaEmpty() throws Exception + public void testReadSchema() throws Exception { IDBSchema schema = DBUtil.readSchema(adapter, getConnection(), SCHEMA_NAME); assertEquals(true, schema.isEmpty()); } - public void testSchemaCreation() throws Exception + public void testCreateSchema() throws Exception { IDBDatabase database = DBUtil.openDatabase(adapter, connectionProvider, SCHEMA_NAME); IDBSchema schema = database.getSchema(); @@ -183,7 +183,7 @@ public abstract class AbstractDBTest extends AbstractOMTest assertEquals(index23.getType(), schema.getTables()[1].getIndex(2).getType()); } - public void testSchemaAddition() throws Exception + public void testChangeSchema() throws Exception { // Init database IDBDatabase database = DBUtil.openDatabase(adapter, connectionProvider, SCHEMA_NAME); @@ -283,4 +283,39 @@ public abstract class AbstractDBTest extends AbstractOMTest assertEquals(index23, schema2.getTables()[1].getIndex(2)); assertEquals(index23.getType(), schema2.getTables()[1].getIndex(2).getType()); } + + public void testEnsureSchema() throws Exception + { + // Init database + IDBDatabase database = DBUtil.openDatabase(adapter, connectionProvider, SCHEMA_NAME); + IDBSchemaTransaction schemaTransaction = database.openSchemaTransaction(); + IDBSchema workingCopy = schemaTransaction.getSchema(); + + IDBTable table1 = workingCopy.addTable("table1"); + table1.addField("field11", DBType.INTEGER, true); + table1.addField("field12", DBType.VARCHAR, 64, true); + table1.addField("field13", DBType.BOOLEAN); + + schemaTransaction.commit(); + + IDBSchema newSchema = DBUtil.createSchema("bla"); + IDBTable table2 = newSchema.addTable("table2"); + IDBField field21 = table2.addField("field21", DBType.INTEGER, true); + IDBField field22 = table2.addField("field22", DBType.VARCHAR, 64, true); + table2.addField("field23", DBType.BOOLEAN); + table2.addIndex("index21", IDBIndex.Type.PRIMARY_KEY, field21, field22); + table2.addIndex("index22", IDBIndex.Type.UNIQUE, field21, field22); + table2.addIndex("index23", IDBIndex.Type.NON_UNIQUE, field22); + + // Reload database + IDBDatabase database2 = DBUtil.openDatabase(adapter, connectionProvider, SCHEMA_NAME); + database2.ensureSchema(newSchema); + + IDBSchema schema2 = database2.getSchema(); + assertEquals(true, schema2.isLocked()); + assertEquals(false, schema2.isEmpty()); + assertEquals(2, schema2.getTables().length); + + DBUtil.dump(schema2); + } } diff --git a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/db/IDBDatabase.java b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/db/IDBDatabase.java index a3ed4dd563..ef313c5004 100644 --- a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/db/IDBDatabase.java +++ b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/db/IDBDatabase.java @@ -11,12 +11,10 @@ package org.eclipse.net4j.db; import org.eclipse.net4j.db.ddl.IDBSchema; -import org.eclipse.net4j.db.ddl.delta.IDBDelta.ChangeKind; +import org.eclipse.net4j.db.ddl.delta.IDBDeltaVisitor; import org.eclipse.net4j.util.collection.Closeable; import org.eclipse.net4j.util.container.IContainer; -import java.util.Map; - /** * @author Eike Stepper * @noimplement This interface is not intended to be implemented by clients. @@ -37,7 +35,9 @@ public interface IDBDatabase extends IContainer<IDBTransaction>, Closeable public IDBSchemaTransaction getSchemaTransaction(); - public void ensureSchema(IDBSchema schema, Map<ChangeKind, Boolean> policy); + public void ensureSchema(IDBSchema schema, IDBDeltaVisitor.Filter.Policy policy); + + public void ensureSchema(IDBSchema schema); public IDBTransaction openTransaction(); diff --git a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/delta/IDBDeltaVisitor.java b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/delta/IDBDeltaVisitor.java index 82a8092411..1bd3bf2b0a 100644 --- a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/delta/IDBDeltaVisitor.java +++ b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/db/ddl/delta/IDBDeltaVisitor.java @@ -20,11 +20,11 @@ import org.eclipse.net4j.internal.db.ddl.delta.DBIndexFieldDelta; import org.eclipse.net4j.internal.db.ddl.delta.DBPropertyDelta; import org.eclipse.net4j.internal.db.ddl.delta.DBSchemaDelta; import org.eclipse.net4j.internal.db.ddl.delta.DBTableDelta; +import org.eclipse.net4j.util.collection.Pair; -import java.util.Collections; +import java.io.Serializable; import java.util.HashMap; import java.util.Map; -import java.util.Set; /** * @since 4.2 @@ -347,30 +347,21 @@ public interface IDBDeltaVisitor */ public static class Filter extends Default { - public static final Map<ChangeKind, Boolean> DEFAULT_POLICY = createPolicy(null, null); + public static final Policy DEFAULT_POLICY = new Policy().allowAll().freeze(); - public static final Boolean ALLOWED = Boolean.TRUE; - - public static final Boolean FORBIDDEN = Boolean.FALSE; - - private Map<ChangeKind, Boolean> policy; + private Policy policy; public Filter() { this(null); } - public Filter(Map<ChangeKind, Boolean> policy) + public Filter(Policy policy) { this.policy = policy == null ? DEFAULT_POLICY : policy; } - public Filter(Set<ChangeKind> ignoredChanges, Set<ChangeKind> forbiddenChanges) - { - this(createPolicy(ignoredChanges, forbiddenChanges)); - } - - public final Map<ChangeKind, Boolean> getPolicy() + public final Policy getPolicy() { return policy; } @@ -516,42 +507,231 @@ public interface IDBDeltaVisitor @Override protected final boolean handle(IDBDelta delta) { - Boolean deltaPolicy = policy.get(delta.getChangeKind()); - if (deltaPolicy == FORBIDDEN) + if (policy.isForbidden(delta)) { throw new ForbiddenChangeException(delta); } - return deltaPolicy == ALLOWED; + if (policy.isAllowed(delta)) + { + return true; + } + + return false; } - public static Map<ChangeKind, Boolean> createPolicy(Set<ChangeKind> ignoredChanges, Set<ChangeKind> forbiddenChanges) + /** + * @author Eike Stepper + */ + public static final class Policy implements Serializable { - if (ignoredChanges == null) + public static final Object ALLOWED = "ALLOWED"; + + public static final Object FORBIDDEN = "FORBIDDEN"; + + public static final Object IGNORED = "IGNORED"; + + private static final long serialVersionUID = 1L; + + private Map<Object, Object> clauses = new HashMap<Object, Object>(); + + private transient boolean frozen; + + public Policy() + { + } + + public boolean isAllowed(DeltaType deltaType) { - ignoredChanges = Collections.emptySet(); + Object value = clauses.get(deltaType); + return value == ALLOWED; } - if (forbiddenChanges == null) + public boolean isAllowed(ChangeKind changeKind) { - forbiddenChanges = Collections.emptySet(); + Object value = clauses.get(changeKind); + return value == ALLOWED; } - Map<ChangeKind, Boolean> policy = new HashMap<IDBDelta.ChangeKind, Boolean>(); - for (ChangeKind changeKind : ChangeKind.values()) + public boolean isAllowed(DeltaType deltaType, ChangeKind changeKind) { - if (!ignoredChanges.contains(changeKind)) + Object value = clauses.get(Pair.create(deltaType, changeKind)); + if (value == null) { - policy.put(changeKind, ALLOWED); + value = clauses.get(deltaType); + if (value == null) + { + value = clauses.get(changeKind); + } } + + return value == ALLOWED; } - for (ChangeKind changeKind : forbiddenChanges) + public boolean isAllowed(IDBDelta delta) { - policy.put(changeKind, FORBIDDEN); + return isAllowed(delta.getDeltaType(), delta.getChangeKind()); } - return policy; + public boolean isForbidden(DeltaType deltaType) + { + Object value = clauses.get(deltaType); + return value == FORBIDDEN; + } + + public boolean isForbidden(ChangeKind changeKind) + { + Object value = clauses.get(changeKind); + return value == FORBIDDEN; + } + + public boolean isForbidden(DeltaType deltaType, ChangeKind changeKind) + { + Object value = clauses.get(Pair.create(deltaType, changeKind)); + if (value == null) + { + value = clauses.get(deltaType); + if (value == null) + { + value = clauses.get(changeKind); + } + } + + return value == FORBIDDEN; + } + + public boolean isForbidden(IDBDelta delta) + { + return isForbidden(delta.getDeltaType(), delta.getChangeKind()); + } + + public boolean isIgnored(DeltaType deltaType) + { + Object value = clauses.get(deltaType); + return value == null || value == IGNORED; + } + + public boolean isIgnored(ChangeKind changeKind) + { + Object value = clauses.get(changeKind); + return value == null || value == IGNORED; + } + + public boolean isIgnored(DeltaType deltaType, ChangeKind changeKind) + { + Object value = clauses.get(Pair.create(deltaType, changeKind)); + if (value == null) + { + value = clauses.get(deltaType); + if (value == null) + { + value = clauses.get(changeKind); + } + } + + return value == null || value == IGNORED; + } + + public boolean isIgnored(IDBDelta delta) + { + return isIgnored(delta.getDeltaType(), delta.getChangeKind()); + } + + public Policy allow(DeltaType deltaType) + { + return addClause(deltaType, ALLOWED); + } + + public Policy allow(ChangeKind changeKind) + { + return addClause(changeKind, ALLOWED); + } + + public Policy allow(DeltaType deltaType, ChangeKind changeKind) + { + return addClause(Pair.create(deltaType, changeKind), ALLOWED); + } + + public Policy allowAll() + { + return ignoreAll().allow(ChangeKind.ADD).allow(ChangeKind.REMOVE).allow(ChangeKind.CHANGE); + } + + public Policy forbid(DeltaType deltaType) + { + return addClause(deltaType, FORBIDDEN); + } + + public Policy forbid(ChangeKind changeKind) + { + return addClause(changeKind, FORBIDDEN); + } + + public Policy forbid(DeltaType deltaType, ChangeKind changeKind) + { + return addClause(Pair.create(deltaType, changeKind), FORBIDDEN); + } + + public Policy forbidAll() + { + return ignoreAll().forbid(ChangeKind.ADD).forbid(ChangeKind.REMOVE).forbid(ChangeKind.CHANGE); + } + + public Policy ignore(DeltaType deltaType) + { + return removeClause(deltaType); + } + + public Policy ignore(ChangeKind changeKind) + { + return removeClause(changeKind); + } + + public Policy ignore(DeltaType deltaType, ChangeKind changeKind) + { + return removeClause(Pair.create(deltaType, changeKind)); + } + + public Policy ignoreAll() + { + checkFrozen(); + clauses.clear(); + return this; + } + + @Override + public String toString() + { + return "Policy" + clauses; + } + + public Policy freeze() + { + frozen = true; + return this; + } + + private void checkFrozen() + { + if (frozen) + { + throw new IllegalStateException("Policy is frozen: " + this); + } + } + + private Policy addClause(Object key, Object value) + { + checkFrozen(); + clauses.put(key, value); + return this; + } + + private Policy removeClause(Object key) + { + checkFrozen(); + clauses.remove(key); + return this; + } } /** @@ -587,14 +767,9 @@ public interface IDBDeltaVisitor { } - public Copier(Map<ChangeKind, Boolean> policies) - { - super(policies); - } - - public Copier(Set<ChangeKind> ignoredChanges, Set<ChangeKind> forbiddenChanges) + public Copier(Policy policy) { - super(ignoredChanges, forbiddenChanges); + super(policy); } public final IDBSchemaDelta getResult() diff --git a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBDatabase.java b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBDatabase.java index 9cef71223d..c1fac3c719 100644 --- a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBDatabase.java +++ b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBDatabase.java @@ -18,7 +18,9 @@ import org.eclipse.net4j.db.IDBSchemaTransaction; import org.eclipse.net4j.db.IDBTransaction; import org.eclipse.net4j.db.ddl.IDBSchema; import org.eclipse.net4j.db.ddl.delta.IDBDelta.ChangeKind; +import org.eclipse.net4j.db.ddl.delta.IDBDelta.DeltaType; import org.eclipse.net4j.db.ddl.delta.IDBDeltaVisitor; +import org.eclipse.net4j.db.ddl.delta.IDBDeltaVisitor.Filter.Policy; import org.eclipse.net4j.db.ddl.delta.IDBSchemaDelta; import org.eclipse.net4j.spi.db.DBAdapter; import org.eclipse.net4j.spi.db.DBSchema; @@ -28,13 +30,15 @@ import org.eclipse.net4j.util.container.SetContainer; import java.sql.Connection; import java.sql.SQLException; import java.util.LinkedList; -import java.util.Map; /** * @author Eike Stepper */ public final class DBDatabase extends SetContainer<IDBTransaction> implements IDBDatabase { + private static final Policy DEFAULT_ENSURE_SCHEMA_POLICY = // + new IDBDeltaVisitor.Filter.Policy().allow(DeltaType.SCHEMA, ChangeKind.CHANGE).allow(ChangeKind.ADD).freeze(); + private DBAdapter adapter; private IDBConnectionProvider connectionProvider; @@ -120,12 +124,7 @@ public final class DBDatabase extends SetContainer<IDBTransaction> implements ID return schemaTransaction; } - public void ensureSchema(IDBSchema schema) - { - ensureSchema(schema, null); - } - - public void ensureSchema(IDBSchema schema, Map<ChangeKind, Boolean> policy) + public void ensureSchema(IDBSchema schema, IDBDeltaVisitor.Filter.Policy policy) { IDBSchemaTransaction schemaTransaction = null; @@ -152,6 +151,11 @@ public final class DBDatabase extends SetContainer<IDBTransaction> implements ID } } + public void ensureSchema(IDBSchema schema) + { + ensureSchema(schema, DEFAULT_ENSURE_SCHEMA_POLICY); + } + public DBTransaction openTransaction() { DBTransaction transaction = new DBTransaction(this); @@ -283,6 +287,12 @@ public final class DBDatabase extends SetContainer<IDBTransaction> implements ID { return --readers > 0; } + + @Override + public String toString() + { + return "READERS[" + readers + "]"; + } } /** @@ -290,5 +300,10 @@ public final class DBDatabase extends SetContainer<IDBTransaction> implements ID */ public final class WriteSchemaAccess implements SchemaAccess { + @Override + public String toString() + { + return "WRITER"; + } } } diff --git a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBSchemaTransaction.java b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBSchemaTransaction.java index 0c584ec41f..acf371ea4b 100644 --- a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBSchemaTransaction.java +++ b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBSchemaTransaction.java @@ -107,10 +107,13 @@ public final class DBSchemaTransaction implements IDBSchemaTransaction, Runnable public void close() { - database.closeSchemaTransaction(); - transaction = null; - oldSchema = null; - schema = null; + if (!isClosed()) + { + database.closeSchemaTransaction(); + transaction = null; + oldSchema = null; + schema = null; + } } public boolean isClosed() diff --git a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/delta/DBDelta.java b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/delta/DBDelta.java index ec00146c92..facd440dd3 100644 --- a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/delta/DBDelta.java +++ b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/delta/DBDelta.java @@ -13,6 +13,7 @@ package org.eclipse.net4j.internal.db.ddl.delta; import org.eclipse.net4j.db.ddl.IDBSchemaElement; import org.eclipse.net4j.db.ddl.delta.IDBDelta; import org.eclipse.net4j.db.ddl.delta.IDBDeltaVisitor; +import org.eclipse.net4j.db.ddl.delta.IDBDeltaVisitor.StopRecursion; import org.eclipse.net4j.spi.db.DBNamedElement; import org.eclipse.net4j.spi.db.DBSchemaElement; import org.eclipse.net4j.util.StringUtil; @@ -83,7 +84,15 @@ public abstract class DBDelta extends DBNamedElement implements IDBDelta public final void accept(IDBDeltaVisitor visitor) { - doAccept(visitor); + try + { + doAccept(visitor); + } + catch (StopRecursion ex) + { + return; + } + for (IDBDelta delta : getElements()) { delta.accept(visitor); diff --git a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/delta/DBTableDelta.java b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/delta/DBTableDelta.java index 1b7e1d0b8d..596ecc5158 100644 --- a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/delta/DBTableDelta.java +++ b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/ddl/delta/DBTableDelta.java @@ -63,7 +63,7 @@ public final class DBTableDelta extends DBDelta implements IDBTableDelta } }); - IDBIndex[] indices = table.getIndices(); + IDBIndex[] indices = table == null ? DBTable.NO_INDICES : table.getIndices(); IDBIndex[] oldIndices = oldTable == null ? DBTable.NO_INDICES : oldTable.getIndices(); compare(indices, oldIndices, new SchemaElementComparator<IDBIndex>() { diff --git a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/collection/Pair.java b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/collection/Pair.java index f22a62398c..9826988662 100644 --- a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/collection/Pair.java +++ b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/collection/Pair.java @@ -97,4 +97,28 @@ public class Pair<T1, T2> { return "Pair[" + element1 + ", " + element2 + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } + + /** + * @since 3.3 + */ + public Pair<T1, T2> copy() + { + return new Pair<T1, T2>(this); + } + + /** + * @since 3.3 + */ + public static <T1, T2> Pair<T1, T2> create() + { + return new Pair<T1, T2>(); + } + + /** + * @since 3.3 + */ + public static <T1, T2> Pair<T1, T2> create(T1 element1, T2 element2) + { + return new Pair<T1, T2>(element1, element2); + } } |