diff options
author | Simon McDuff | 2008-06-13 20:33:50 +0000 |
---|---|---|
committer | Simon McDuff | 2008-06-13 20:33:50 +0000 |
commit | 9bbbde7205f79ad16a06322bbb64f5b7025dd4a8 (patch) | |
tree | 53495acdc5392899a2f191be6a3c3510a002c78a | |
parent | bdbfa6d5b823d53f661b86998bdb1ff5772b7e4d (diff) | |
download | cdo-9bbbde7205f79ad16a06322bbb64f5b7025dd4a8.tar.gz cdo-9bbbde7205f79ad16a06322bbb64f5b7025dd4a8.tar.xz cdo-9bbbde7205f79ad16a06322bbb64f5b7025dd4a8.zip |
[233490] Change Subscription
https://bugs.eclipse.org/bugs/show_bug.cgi?id=233490
31 files changed, 1699 insertions, 39 deletions
diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/CDOProtocolConstants.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/CDOProtocolConstants.java index 39a2be0089..54f4556a24 100644 --- a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/CDOProtocolConstants.java +++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/CDOProtocolConstants.java @@ -9,6 +9,8 @@ * Eike Stepper - initial API and implementation * Simon McDuff - 230832: Make remote invalidation configurable * https://bugs.eclipse.org/bugs/show_bug.cgi?id=230832 + * Simon McDuff - 233490: Change Subscription + * https://bugs.eclipse.org/bugs/show_bug.cgi?id=233490 **************************************************************************/ package org.eclipse.emf.cdo.common; @@ -53,7 +55,9 @@ public interface CDOProtocolConstants public static final short SIGNAL_SYNC = 17; - public static final short SIGNAL_AUTOMATIC_REFRESH = 18; + public static final short SIGNAL_PASSIVE_UPDATE = 18; + + public static final short SIGNAL_CHANGE_SUBSCRIPTION = 19; public static final int ERROR_REPOSITORY_NOT_FOUND = -1; diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/NotificationManager.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/NotificationManager.java new file mode 100644 index 0000000000..b06e9e73bb --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/NotificationManager.java @@ -0,0 +1,56 @@ +/*************************************************************************** + * Copyright (c) 2004 - 2008 Eike Stepper, Germany. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Simon McDuff - initial API and implementation + **************************************************************************/ +package org.eclipse.emf.cdo.internal.server; + +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta; +import org.eclipse.emf.cdo.server.INotificationManager; +import org.eclipse.emf.cdo.server.IStoreWriter.CommitContext; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Simon McDuff + */ +public class NotificationManager implements INotificationManager +{ + private Repository repository = null; + + public NotificationManager(Repository repository) + { + this.repository = repository; + } + + public void notifyInvalidation(Session session, CommitContext commitContext) + { + CDORevisionDelta[] dirtyObjectDeltas = commitContext.getDirtyObjectDeltas(); + + int modifications = dirtyObjectDeltas.length; + + if (modifications > 0) + { + List<CDOID> dirtyIDs = new ArrayList<CDOID>(modifications); + List<CDORevisionDelta> deltas = new ArrayList<CDORevisionDelta>(modifications); + + for (int i = 0; i < modifications; i++) + { + dirtyIDs.add(dirtyObjectDeltas[i].getID()); + deltas.add(dirtyObjectDeltas[i]); + } + + SessionManager sessionManager = (SessionManager)repository.getSessionManager(); + + sessionManager.notifyInvalidation(commitContext.getTimeStamp(), dirtyIDs, deltas, session); + } + + } +} diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Repository.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Repository.java index 2d3c706638..885b8f7214 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Repository.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Repository.java @@ -10,7 +10,8 @@ * Simon McDuff - https://bugs.eclipse.org/bugs/show_bug.cgi?id=201266 * Simon McDuff - 233273: [QUERY] Develop Query mechanism * https://bugs.eclipse.org/bugs/show_bug.cgi?id=233273 - * + * Simon McDuff - 233490: Change Subscription + * https://bugs.eclipse.org/bugs/show_bug.cgi?id=233490 **************************************************************************/ package org.eclipse.emf.cdo.internal.server; @@ -58,6 +59,8 @@ public class Repository extends Container<IRepositoryElement> implements IReposi private RevisionManager revisionManager = createRevisionManager(); private QueryManager queryManager = createQueryManager(); + + private NotificationManager notificationManager = createNotificationManager(); private IRepositoryElement[] elements; @@ -172,6 +175,11 @@ public class Repository extends Container<IRepositoryElement> implements IReposi return queryManager; } + public NotificationManager getNotificationManager() + { + return notificationManager; + } + public IRepositoryElement[] getElements() { return elements; @@ -230,6 +238,10 @@ public class Repository extends Container<IRepositoryElement> implements IReposi { return new QueryManager(); } + protected NotificationManager createNotificationManager() + { + return new NotificationManager(this); + } @Override protected void doBeforeActivate() throws Exception { diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Session.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Session.java index 5d701197d0..a8f816900c 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Session.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Session.java @@ -10,6 +10,8 @@ * Simon McDuff - https://bugs.eclipse.org/bugs/show_bug.cgi?id=201266 * Simon McDuff - 230832: Make remote invalidation configurable * https://bugs.eclipse.org/bugs/show_bug.cgi?id=230832 + * Simon McDuff - 233490: Change Subscription + * https://bugs.eclipse.org/bugs/show_bug.cgi?id=233490 **************************************************************************/ package org.eclipse.emf.cdo.internal.server; @@ -21,6 +23,7 @@ import org.eclipse.emf.cdo.common.id.CDOIDProvider; import org.eclipse.emf.cdo.common.model.CDOClass; import org.eclipse.emf.cdo.common.model.CDOClassRef; import org.eclipse.emf.cdo.common.model.CDOFeature; +import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta; import org.eclipse.emf.cdo.internal.server.bundle.OM; import org.eclipse.emf.cdo.internal.server.protocol.CDOServerProtocol; import org.eclipse.emf.cdo.internal.server.protocol.InvalidationNotification; @@ -37,6 +40,7 @@ import org.eclipse.net4j.util.lifecycle.ILifecycle; import org.eclipse.net4j.util.lifecycle.LifecycleEventAdapter; import java.text.MessageFormat; +import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -52,7 +56,7 @@ public class Session extends Container<IView> implements ISession, CDOIDProvider private CDOServerProtocol protocol; private int sessionID; - + private boolean passiveUpdateEnabled = true; private boolean legacySupportEnabled; @@ -201,14 +205,28 @@ public class Session extends Container<IView> implements ISession, CDOIDProvider return new View(this, viewID, type); } - public void notifyInvalidation(long timeStamp, List<CDOID> dirtyIDs) - { - if (!isPassiveUpdateEnabled()) - return; - + public void notifyInvalidation(long timeStamp, List<CDOID> dirtyIDs, List<CDORevisionDelta> deltas) + { + if (!isPassiveUpdateEnabled()) dirtyIDs = new ArrayList<CDOID>(); + + // Look if someone needs to know something about modified objects + List<CDORevisionDelta> newDeltas = new ArrayList<CDORevisionDelta>(); + for (CDORevisionDelta delta : deltas) + { + CDOID lookupID = delta.getID(); + for (IView view : views.values()) + { + if (((View)view).isSubscribe(lookupID)) + { + newDeltas.add(delta); + break; + } + } + } try { - new InvalidationNotification(protocol.getChannel(), timeStamp, dirtyIDs).send(); + if (dirtyIDs.size() > 0 || newDeltas.size() > 0) + new InvalidationNotification(protocol.getChannel(), this, timeStamp, dirtyIDs, newDeltas).send(); } catch (Exception ex) { diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/SessionManager.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/SessionManager.java index 02cfb5ad0a..186033a0a8 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/SessionManager.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/SessionManager.java @@ -13,6 +13,7 @@ package org.eclipse.emf.cdo.internal.server; import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta; import org.eclipse.emf.cdo.internal.server.bundle.OM; import org.eclipse.emf.cdo.internal.server.protocol.CDOServerProtocol; import org.eclipse.emf.cdo.server.ISession; @@ -104,13 +105,13 @@ public class SessionManager extends Container<ISession> implements ISessionManag } } - public void notifyInvalidation(long timeStamp, List<CDOID> dirtyIDs, Session excludedSession) + public void notifyInvalidation(long timeStamp, List<CDOID> dirtyIDs, List<CDORevisionDelta> deltas, Session excludedSession) { for (Session session : getSessions()) { if (session != excludedSession) { - session.notifyInvalidation(timeStamp, dirtyIDs); + session.notifyInvalidation(timeStamp, dirtyIDs, deltas); } } } diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Transaction.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Transaction.java index 25cc439d40..423f7bbce8 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Transaction.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Transaction.java @@ -7,6 +7,8 @@ * * Contributors: * Eike Stepper - initial API and implementation + * Simon McDuff - 233490: Change Subscription + * https://bugs.eclipse.org/bugs/show_bug.cgi?id=233490 **************************************************************************/ package org.eclipse.emf.cdo.internal.server; @@ -196,17 +198,9 @@ public class Transaction extends View implements ITransaction, IStoreWriter.Comm { try { - int modifications = dirtyObjectDeltas.length; - if (success && modifications > 0) + if (success) { - List<CDOID> dirtyIDs = new ArrayList<CDOID>(modifications); - for (int i = 0; i < modifications; i++) - { - dirtyIDs.add(dirtyObjectDeltas[i].getID()); - } - - SessionManager sessionManager = (SessionManager)repository.getSessionManager(); - sessionManager.notifyInvalidation(timeStamp, dirtyIDs, getSession()); + repository.getNotificationManager().notifyInvalidation( getSession(), this); } } finally diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/View.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/View.java index eeb793b4e9..e142ce9b1a 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/View.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/View.java @@ -7,12 +7,17 @@ * * Contributors: * Eike Stepper - initial API and implementation + * Simon McDuff - 233490: Change Subscription + * https://bugs.eclipse.org/bugs/show_bug.cgi?id=233490 **************************************************************************/ package org.eclipse.emf.cdo.internal.server; +import org.eclipse.emf.cdo.common.id.CDOID; import org.eclipse.emf.cdo.server.IView; import java.text.MessageFormat; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; /** * @author Eike Stepper @@ -25,6 +30,8 @@ public class View implements IView private Type viewType; + private Map<CDOID, CDOID> changeSubscriptionObjects = new ConcurrentHashMap<CDOID, CDOID>(); + public View(Session session, int viewID, Type viewType) { this.session = session; @@ -42,6 +49,26 @@ public class View implements IView return viewID; } + public void subscribe(CDOID id) + { + changeSubscriptionObjects.put(id, id); + } + + public void unsubscribe(CDOID id) + { + changeSubscriptionObjects.remove(id); + } + + public boolean isSubscribe(CDOID id) + { + return changeSubscriptionObjects.get(id) != null; + } + + public void clearChangeSubscription() + { + changeSubscriptionObjects.clear(); + } + public Type getViewType() { return viewType; diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/CDOServerProtocol.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/CDOServerProtocol.java index 9d1821382c..2afe29cc70 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/CDOServerProtocol.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/CDOServerProtocol.java @@ -11,6 +11,8 @@ * https://bugs.eclipse.org/bugs/show_bug.cgi?id=233273 * Simon McDuff - 230832: Make remote invalidation configurable * https://bugs.eclipse.org/bugs/show_bug.cgi?id=230832 + * Simon McDuff - 233490: Change Subscription + * https://bugs.eclipse.org/bugs/show_bug.cgi?id=233490 **************************************************************************/ package org.eclipse.emf.cdo.internal.server.protocol; @@ -88,9 +90,12 @@ public class CDOServerProtocol extends CDOProtocolImpl case CDOProtocolConstants.SIGNAL_SYNC: return new SyncRevisionIndication(); - case CDOProtocolConstants.SIGNAL_AUTOMATIC_REFRESH: + case CDOProtocolConstants.SIGNAL_PASSIVE_UPDATE: return new PassiveUpdateIndication(); + case CDOProtocolConstants.SIGNAL_CHANGE_SUBSCRIPTION: + return new ChangeSubscriptionIndication(); + default: return null; diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/ChangeSubscriptionIndication.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/ChangeSubscriptionIndication.java new file mode 100644 index 0000000000..ca554f086b --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/ChangeSubscriptionIndication.java @@ -0,0 +1,81 @@ +/*************************************************************************** + * Copyright (c) 2004 - 2008 Eike Stepper, Germany. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Simon McDuff - initial API and implementation + **************************************************************************/ +package org.eclipse.emf.cdo.internal.server.protocol; + +import org.eclipse.emf.cdo.common.CDOProtocolConstants; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.id.CDOIDObjectFactory; +import org.eclipse.emf.cdo.common.id.CDOIDUtil; +import org.eclipse.emf.cdo.internal.server.bundle.OM; +import org.eclipse.emf.cdo.server.IView; + +import org.eclipse.net4j.util.io.ExtendedDataInputStream; +import org.eclipse.net4j.util.io.ExtendedDataOutputStream; +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import java.io.IOException; + +/** + * @author Simon McDuff + */ +public class ChangeSubscriptionIndication extends CDOReadIndication +{ + private static final ContextTracer PROTOCOL = new ContextTracer(OM.DEBUG_PROTOCOL, ChangeSubscriptionIndication.class); + + public ChangeSubscriptionIndication() + { + } + + @Override + protected short getSignalID() + { + return CDOProtocolConstants.SIGNAL_CHANGE_SUBSCRIPTION; + } + + @Override + protected void indicating(ExtendedDataInputStream in) throws IOException + { + CDOIDObjectFactory factory = getStore().getCDOIDObjectFactory(); + + int viewID = in.readInt(); + boolean clear = in.readBoolean(); + + int size = in.readInt(); + boolean registered = true; + if (size <= 0) + { + registered = false; + size = -size; + } + + IView view = getSession().getView(viewID); + + if (clear) + { + view.clearChangeSubscription(); + } + + for (int i = 0; i < size; i++) + { + CDOID id = CDOIDUtil.read(in, factory); + if (registered) + view.subscribe(id); + else + view.unsubscribe(id); + } + } + + @Override + protected void responding(ExtendedDataOutputStream out) throws IOException + { + out.writeBoolean(true); + } +} diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/InvalidationNotification.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/InvalidationNotification.java index 1a6c64fa7d..44b23fb624 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/InvalidationNotification.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/InvalidationNotification.java @@ -8,12 +8,16 @@ * Contributors: * Eike Stepper - initial API and implementation * Simon McDuff - https://bugs.eclipse.org/bugs/show_bug.cgi?id=201266 + * Simon McDuff - 233490: Change Subscription + * https://bugs.eclipse.org/bugs/show_bug.cgi?id=233490 **************************************************************************/ package org.eclipse.emf.cdo.internal.server.protocol; import org.eclipse.emf.cdo.common.CDOProtocolConstants; import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.id.CDOIDProvider; import org.eclipse.emf.cdo.common.id.CDOIDUtil; +import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta; import org.eclipse.emf.cdo.internal.server.bundle.OM; import org.eclipse.net4j.channel.IChannel; @@ -31,15 +35,21 @@ public class InvalidationNotification extends Request { private static final ContextTracer PROTOCOL = new ContextTracer(OM.DEBUG_PROTOCOL, InvalidationNotification.class); + private CDOIDProvider provider; + private long timeStamp; private List<CDOID> dirtyIDs; + + private List<CDORevisionDelta> deltas; - public InvalidationNotification(IChannel channel, long timeStamp, List<CDOID> dirtyIDs) + public InvalidationNotification(IChannel channel, CDOIDProvider provider, long timeStamp, List<CDOID> dirtyIDs, List<CDORevisionDelta> deltas) { super(channel); + this.provider = provider; this.timeStamp = timeStamp; this.dirtyIDs = dirtyIDs; + this.deltas = deltas; } @Override @@ -62,10 +72,18 @@ public class InvalidationNotification extends Request PROTOCOL.format("Writing {0} dirty IDs", dirtyIDs.size()); } - out.writeInt(dirtyIDs.size()); + out.writeInt(dirtyIDs == null ? 0 : dirtyIDs.size()); + for (CDOID dirtyID : dirtyIDs) { CDOIDUtil.write(out, dirtyID); } + + out.writeInt(deltas == null ? 0 : deltas.size()); + + for (CDORevisionDelta delta : deltas) + { + delta.write(out, provider); + } } } diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/OpenSessionIndication.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/OpenSessionIndication.java index 8c09b314f2..49b6930cc5 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/OpenSessionIndication.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/OpenSessionIndication.java @@ -41,6 +41,8 @@ public class OpenSessionIndication extends IndicationWithResponse private String repositoryName; private boolean legacySupportEnabled; + + private boolean passiveUpdateEnabled; public OpenSessionIndication() { @@ -66,7 +68,13 @@ public class OpenSessionIndication extends IndicationWithResponse { PROTOCOL.format("Read legacySupportEnabled: {0}", legacySupportEnabled); } - } + + passiveUpdateEnabled = in.readBoolean(); + if (PROTOCOL.isEnabled()) + { + PROTOCOL.format("Read passiveUpdateEnabled: {0}", passiveUpdateEnabled); + } +} @Override protected void responding(ExtendedDataOutputStream out) throws IOException @@ -78,6 +86,9 @@ public class OpenSessionIndication extends IndicationWithResponse CDOServerProtocol serverProtocol = (CDOServerProtocol)getProtocol(); Session session = sessionManager.openSession(serverProtocol, legacySupportEnabled); + + session.setPassiveUpdateEnabled(passiveUpdateEnabled); + serverProtocol.setInfraStructure(session); writeSessionID(out, session); diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/PassiveUpdateIndication.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/PassiveUpdateIndication.java index e9a061ba44..5920104409 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/PassiveUpdateIndication.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/protocol/PassiveUpdateIndication.java @@ -35,7 +35,7 @@ public class PassiveUpdateIndication extends SyncRevisionIndication @Override protected short getSignalID() { - return CDOProtocolConstants.SIGNAL_AUTOMATIC_REFRESH; + return CDOProtocolConstants.SIGNAL_PASSIVE_UPDATE; } @Override protected void indicating(ExtendedDataInputStream in) throws IOException diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/INotificationManager.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/INotificationManager.java new file mode 100644 index 0000000000..5bb5b84a16 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/INotificationManager.java @@ -0,0 +1,24 @@ +/*************************************************************************** + * Copyright (c) 2004 - 2008 Eike Stepper, Germany. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Simon McDuff - initial API and implementation + **************************************************************************/ +package org.eclipse.emf.cdo.server; + +import org.eclipse.emf.cdo.internal.server.Session; +import org.eclipse.emf.cdo.server.IStoreWriter.CommitContext; + +/** + * @author Simon McDuff + */ +public interface INotificationManager +{ + + public abstract void notifyInvalidation(Session session, CommitContext commitContext); + +} diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepository.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepository.java index 89c5f7c7f4..c322ffb826 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepository.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepository.java @@ -11,6 +11,7 @@ package org.eclipse.emf.cdo.server; import org.eclipse.emf.cdo.common.id.CDOIDMetaRange; +import org.eclipse.emf.cdo.internal.server.NotificationManager; import org.eclipse.net4j.util.container.IContainer; @@ -48,6 +49,8 @@ public interface IRepository extends IContainer<IRepositoryElement> public IResourceManager getResourceManager(); public IRevisionManager getRevisionManager(); + + public INotificationManager getNotificationManager(); public long getLastMetaID(); diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IView.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IView.java index d39dd0cdb4..b4b35e09e8 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IView.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IView.java @@ -11,6 +11,7 @@ package org.eclipse.emf.cdo.server; import org.eclipse.emf.cdo.common.CDOProtocolView; +import org.eclipse.emf.cdo.common.id.CDOID; /** * @author Eike Stepper @@ -18,4 +19,7 @@ import org.eclipse.emf.cdo.common.CDOProtocolView; public interface IView extends CDOProtocolView { public ISession getSession(); + public void subscribe(CDOID id); + public void unsubscribe(CDOID id); + public void clearChangeSubscription(); } diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/AllTests.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/AllTests.java index feeb57cbe0..62611fe2e3 100644 --- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/AllTests.java +++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/AllTests.java @@ -41,6 +41,8 @@ public class AllTests suite.addTestSuite(IndexReconstructionTest.class); suite.addTestSuite(NoLegacyTest.class); suite.addTestSuite(SavePointTest.class); + suite.addTestSuite(ChangeSubscriptionTest.class); + suite.addTestSuite(QueryTest.class); // TODO suite.addTestSuite(GeneratedEcoreTest.class); // $JUnit-END$ diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/ChangeSubscriptionTest.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/ChangeSubscriptionTest.java new file mode 100644 index 0000000000..09399015de --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/ChangeSubscriptionTest.java @@ -0,0 +1,575 @@ +/*************************************************************************** + * Copyright (c) 2004 - 2008 Eike Stepper, Germany. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Simon McDuff - initial API and implementation + **************************************************************************/ +package org.eclipse.emf.cdo.tests; + +import org.eclipse.emf.cdo.CDOChangeSubscriptionPolicy; +import org.eclipse.emf.cdo.CDONotification; +import org.eclipse.emf.cdo.CDOSession; +import org.eclipse.emf.cdo.CDOTransaction; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.eresource.CDOResource; +import org.eclipse.emf.cdo.tests.model1.Category; +import org.eclipse.emf.cdo.tests.model1.Company; +import org.eclipse.emf.cdo.tests.model1.Model1Factory; +import org.eclipse.emf.cdo.tests.model1.Model1Package; + +import org.eclipse.emf.internal.cdo.InternalCDOObject; + +import org.eclipse.emf.common.notify.Adapter; +import org.eclipse.emf.common.notify.Notification; +import org.eclipse.emf.common.notify.Notifier; +import org.eclipse.emf.ecore.EObject; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * @author Simon McDuff + */ +public class ChangeSubscriptionTest extends AbstractCDOTest +{ + static class TestAdapter implements Adapter + { + List<Notification> notifications = new ArrayList<Notification>(); + + Notifier notifier; + + public Notifier getTarget() + { + return notifier; + } + + public List<Notification> getNotifications() + { + return notifications; + } + + public boolean isAdapterForType(Object type) + { + return false; + } + + public void notifyChanged(Notification notification) + { + notifications.add(notification); + } + + public void setTarget(Notifier newTarget) + { + this.notifier = newTarget; + } + + } + + public void testSameSession() throws Exception + { + testSameSession(CDOChangeSubscriptionPolicy.ALL); + } + + public void testSameSession_disable() throws Exception + { + testSameSession(CDOChangeSubscriptionPolicy.NONE); + } + + public void testSameSession(final CDOChangeSubscriptionPolicy enabled) throws Exception + { + msg("Opening session"); + final CDOSession session = openModel1Session(); + + session.setPassiveUpdateEnabled(false); + + // ************************************************************* // + + msg("Creating category1"); + final Category category1A = Model1Factory.eINSTANCE.createCategory(); + category1A.setName("category1"); + + msg("Creating company"); + final Company companyA = Model1Factory.eINSTANCE.createCompany(); + + msg("Adding categories"); + companyA.getCategories().add(category1A); + + msg("Opening transaction"); + final CDOTransaction transaction = session.openTransaction(); + + transaction.setChangeSubscriptionPolicy(enabled); + + msg("Creating resource"); + final CDOResource resourceA = transaction.createResource("/test1"); + + msg("Adding company"); + resourceA.getContents().add(companyA); + + msg("Committing"); + transaction.commit(); + final TestAdapter adapter = new TestAdapter(); + category1A.eAdapters().add(adapter); + + // ************************************************************* // + + msg("Opening view"); + final CDOTransaction transaction2 = session.openTransaction(); + + final Category category1B = (Category)transaction2.getObject(category1A.cdoID(), true); + + msg("Changing name"); + category1B.setName("CHANGED NAME"); + + assertEquals(0, adapter.getNotifications().size()); + + msg("Committing"); + transaction2.commit(); + + msg("Checking after commit"); + boolean timedOut = new PollingTimeOuter(10, 100) + { + @Override + protected boolean successful() + { + return (enabled == CDOChangeSubscriptionPolicy.ALL && adapter.getNotifications().size() == 1) + || (enabled == CDOChangeSubscriptionPolicy.NONE && adapter.getNotifications().size() == 0); + } + }.timedOut(); + + assertEquals(false, timedOut); + + // Switching policy to the other + final CDOChangeSubscriptionPolicy enabled2 = enabled == CDOChangeSubscriptionPolicy.ALL ? CDOChangeSubscriptionPolicy.NONE + : CDOChangeSubscriptionPolicy.ALL; + + transaction.setChangeSubscriptionPolicy(enabled2); + + adapter.getNotifications().clear(); + + msg("Changing name"); + category1B.setName("CHANGED NAME_VERSION 2"); + + assertEquals(0, adapter.getNotifications().size()); + + msg("Committing"); + transaction2.commit(); + + msg("Checking after commit"); + timedOut = new PollingTimeOuter(10, 100) + { + @Override + protected boolean successful() + { + return (enabled2 == CDOChangeSubscriptionPolicy.ALL && adapter.getNotifications().size() == 1) + || (enabled2 == CDOChangeSubscriptionPolicy.NONE && adapter.getNotifications().size() == 0); + } + }.timedOut(); + + assertEquals(false, timedOut); + } + + public void testSeparateSession() throws Exception + { + testSeparateSession(CDOChangeSubscriptionPolicy.ALL); + + } + + public void testSeparateSession_disable() throws Exception + { + testSeparateSession(CDOChangeSubscriptionPolicy.NONE); + } + + public void testSeparateSession(final CDOChangeSubscriptionPolicy enabled) throws Exception + { + msg("Opening session"); + final CDOSession session = openModel1Session(); + + session.setPassiveUpdateEnabled(false); + + // ************************************************************* // + + msg("Creating category1"); + final Category category1A = Model1Factory.eINSTANCE.createCategory(); + category1A.setName("category1"); + + msg("Creating company"); + final Company companyA = Model1Factory.eINSTANCE.createCompany(); + + msg("Adding categories"); + companyA.getCategories().add(category1A); + + msg("Opening transaction"); + final CDOTransaction transaction = session.openTransaction(); + transaction.setChangeSubscriptionPolicy(enabled); + + msg("Creating resource"); + final CDOResource resourceA = transaction.createResource("/test1"); + + msg("Adding company"); + resourceA.getContents().add(companyA); + + msg("Committing"); + transaction.commit(); + final TestAdapter adapter = new TestAdapter(); + category1A.eAdapters().add(adapter); + + // ************************************************************* // + + msg("Opening view"); + final CDOSession session2 = openModel1Session(); + session2.setPassiveUpdateEnabled(false); + + final CDOTransaction transaction2 = session2.openTransaction(); + + final Category category1B = (Category)transaction2.getObject(category1A.cdoID(), true); + + msg("Changing name"); + category1B.setName("CHANGED NAME"); + + assertEquals(0, adapter.getNotifications().size()); + + msg("Committing"); + transaction2.commit(); + + msg("Checking after commit"); + boolean timedOut = new PollingTimeOuter(10, 100) + { + @Override + protected boolean successful() + { + return (enabled == CDOChangeSubscriptionPolicy.ALL && adapter.getNotifications().size() == 1) + || (enabled == CDOChangeSubscriptionPolicy.NONE && adapter.getNotifications().size() == 0); + } + }.timedOut(); + + assertEquals(false, timedOut); + + // Switching policy to the other + final CDOChangeSubscriptionPolicy enabled2 = enabled == CDOChangeSubscriptionPolicy.ALL ? CDOChangeSubscriptionPolicy.NONE + : CDOChangeSubscriptionPolicy.ALL; + + transaction.setChangeSubscriptionPolicy(enabled2); + + adapter.getNotifications().clear(); + + msg("Changing name"); + category1B.setName("CHANGED NAME_VERSION 2"); + + assertEquals(0, adapter.getNotifications().size()); + + msg("Committing"); + transaction2.commit(); + + msg("Checking after commit"); + timedOut = new PollingTimeOuter(10, 100) + { + @Override + protected boolean successful() + { + return (enabled2 == CDOChangeSubscriptionPolicy.ALL && adapter.getNotifications().size() == 1) + || (enabled2 == CDOChangeSubscriptionPolicy.NONE && adapter.getNotifications().size() == 0); + } + }.timedOut(); + + assertEquals(false, timedOut); + + } + + public void testTemporaryObject() throws Exception + { + msg("Opening session"); + final CDOSession session = openModel1Session(); + + session.setPassiveUpdateEnabled(false); + + // ************************************************************* // + + msg("Creating category1"); + final Category category1A = Model1Factory.eINSTANCE.createCategory(); + category1A.setName("category1"); + + msg("Creating company"); + final Company companyA = Model1Factory.eINSTANCE.createCompany(); + + msg("Adding categories"); + companyA.getCategories().add(category1A); + + msg("Opening transaction"); + final CDOTransaction transaction = session.openTransaction(); + transaction.setChangeSubscriptionPolicy(CDOChangeSubscriptionPolicy.ALL); + msg("Creating resource"); + final CDOResource resourceA = transaction.createResource("/test1"); + + msg("Adding company"); + resourceA.getContents().add(companyA); + + msg("Committing"); + + final TestAdapter adapter = new TestAdapter(); + category1A.eAdapters().add(adapter); + + transaction.commit(); + + // ************************************************************* // + + msg("Opening view"); + final CDOSession session2 = openModel1Session(); + session2.setPassiveUpdateEnabled(false); + + final CDOTransaction transaction2 = session2.openTransaction(); + transaction.setChangeSubscriptionPolicy(CDOChangeSubscriptionPolicy.ALL); + + final Category category1B = (Category)transaction2.getObject(category1A.cdoID(), true); + + msg("Changing name"); + category1B.setName("CHANGED NAME"); + + assertEquals(0, adapter.getNotifications().size()); + + msg("Committing"); + transaction2.commit(); + + msg("Checking after commit"); + boolean timedOut = new PollingTimeOuter(10, 100) + { + @Override + protected boolean successful() + { + return adapter.getNotifications().size() == 1; + } + }.timedOut(); + + assertEquals(false, timedOut); + } + + class CDOIDFilterChangeSubscriptionPolicy implements CDOChangeSubscriptionPolicy + { + private Set<CDOID> cdoIDs = new HashSet<CDOID>(); + + public boolean valid(EObject eObject, Adapter object) + { + return cdoIDs.contains(((InternalCDOObject)eObject).cdoID()); + } + + public Set<CDOID> getCdoIDs() + { + return cdoIDs; + } + } + + public void testSeparateSession_CUSTOM() throws Exception + { + + CDOIDFilterChangeSubscriptionPolicy customPolicy = new CDOIDFilterChangeSubscriptionPolicy(); + + msg("Opening session"); + final CDOSession session = openModel1Session(); + + session.setPassiveUpdateEnabled(false); + + // ************************************************************* // + + msg("Creating category1"); + final Category category1A = Model1Factory.eINSTANCE.createCategory(); + category1A.setName("category1"); + + msg("Creating company"); + final Company companyA = Model1Factory.eINSTANCE.createCompany(); + + msg("Adding categories"); + companyA.getCategories().add(category1A); + + msg("Opening transaction"); + final CDOTransaction transaction = session.openTransaction(); + + transaction.setChangeSubscriptionPolicy(customPolicy); + + msg("Creating resource"); + final CDOResource resourceA = transaction.createResource("/test1"); + + msg("Adding company"); + resourceA.getContents().add(companyA); + + msg("Committing"); + transaction.commit(); + + final TestAdapter adapter = new TestAdapter(); + + customPolicy.getCdoIDs().add(category1A.cdoID()); + + category1A.eAdapters().add(adapter); + companyA.eAdapters().add(adapter); + + // ************************************************************* // + + msg("Opening view"); + final CDOSession session2 = openModel1Session(); + session2.setPassiveUpdateEnabled(false); + + final CDOTransaction transaction2 = session2.openTransaction(); + + final Category category1B = (Category)transaction2.getObject(category1A.cdoID(), true); + final Company company1B = (Company)transaction2.getObject(companyA.cdoID(), true); + + msg("Changing name"); + category1B.setName("CHANGED NAME"); + company1B.setName("TEST1"); + + assertEquals(0, adapter.getNotifications().size()); + + msg("Committing"); + transaction2.commit(); + + msg("Checking after commit"); + boolean timedOut = new PollingTimeOuter(5, 200) + { + @Override + protected boolean successful() + { + return adapter.getNotifications().size() == 1; + } + }.timedOut(); + + assertEquals(false, timedOut); + + // Switching policy to the other + transaction.setChangeSubscriptionPolicy(CDOChangeSubscriptionPolicy.ALL); + + adapter.getNotifications().clear(); + + msg("Changing name"); + category1B.setName("CHANGED NAME_VERSION 2"); + company1B.setName("TEST2"); + + assertEquals(0, adapter.getNotifications().size()); + + msg("Committing"); + transaction2.commit(); + + msg("Checking after commit"); + timedOut = new PollingTimeOuter(10, 100) + { + @Override + protected boolean successful() + { + return adapter.getNotifications().size() == 2; + } + }.timedOut(); + + assertEquals(false, timedOut); + + } + + public void testNotificationChain() throws Exception + { + msg("Opening session"); + final CDOSession session = openModel1Session(); + + session.setPassiveUpdateEnabled(false); + + // ************************************************************* // + + msg("Creating category1"); + final Category category1A = Model1Factory.eINSTANCE.createCategory(); + category1A.setName("category1"); + + msg("Creating company"); + final Company companyA = Model1Factory.eINSTANCE.createCompany(); + + msg("Adding categories"); + companyA.getCategories().add(category1A); + + msg("Opening transaction"); + final CDOTransaction transaction = session.openTransaction(); + + transaction.setChangeSubscriptionPolicy(CDOChangeSubscriptionPolicy.ALL); + + msg("Creating resource"); + final CDOResource resourceA = transaction.createResource("/test1"); + + msg("Adding company"); + resourceA.getContents().add(companyA); + + msg("Committing"); + transaction.commit(); + + final TestAdapter adapter = new TestAdapter(); + + companyA.eAdapters().add(adapter); + + // ************************************************************* // + + msg("Opening view"); + final CDOSession session2 = openModel1Session(); + session2.setPassiveUpdateEnabled(false); + + final CDOTransaction transaction2 = session2.openTransaction(); + + final Company company1B = (Company)transaction2.getObject(companyA.cdoID(), true); + + msg("Changing name"); + company1B.setName("TEST1"); + company1B.setCity("CITY1"); + + final Category category2B = Model1Factory.eINSTANCE.createCategory(); + company1B.getCategories().add(category2B); + + assertEquals(0, adapter.getNotifications().size()); + + msg("Committing"); + transaction2.commit(); + + msg("Checking after commit"); + boolean timedOut = new PollingTimeOuter(5, 200) + { + @Override + protected boolean successful() + { + return adapter.getNotifications().size() == 3; + } + }.timedOut(); + + assertEquals(false, timedOut); + int count = 0; + for (Notification notification : adapter.getNotifications()) + { + CDONotification cdoNotification = (CDONotification)notification; + + if (adapter.getNotifications().size() - 1 == count) + assertEquals(false,cdoNotification.hasNext()); + else + assertEquals(true,cdoNotification.hasNext()); + + + if (notification.getFeature() == Model1Package.eINSTANCE.getCategory_Name()) + { + assertEquals(Notification.SET, notification.getEventType()); + assertEquals("TEST1", notification.getNewStringValue()); + } + else if (notification.getFeature() == Model1Package.eINSTANCE.getAddress_City()) + { + assertEquals(Notification.SET, notification.getEventType()); + assertEquals("CITY1", notification.getNewStringValue()); + } + else if (notification.getFeature() == Model1Package.eINSTANCE.getCompany_Categories()) + { + assertEquals(Notification.ADD, notification.getEventType()); + assertEquals(1, notification.getPosition()); + assertEquals(transaction.getObject(category2B.cdoID(), true), notification.getNewValue()); + + } + else + assertEquals(false, false); + + count++; + + } + + } +} diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/CDOChangeSubscriptionPolicy.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/CDOChangeSubscriptionPolicy.java new file mode 100644 index 0000000000..ad727af420 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/CDOChangeSubscriptionPolicy.java @@ -0,0 +1,38 @@ +/*************************************************************************** + * Copyright (c) 2004 - 2008 Eike Stepper, Germany. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Simon McDuff - initial API and implementation + **************************************************************************/ +package org.eclipse.emf.cdo; + +import org.eclipse.emf.common.notify.Adapter; +import org.eclipse.emf.ecore.EObject; + +/** + * @author Simon McDuff + */ +public interface CDOChangeSubscriptionPolicy +{ + public static CDOChangeSubscriptionPolicy NONE = new CDOChangeSubscriptionPolicy() + { + public boolean valid(EObject eObject, Adapter adapter) + { + return false; + } + }; + + public static CDOChangeSubscriptionPolicy ALL = new CDOChangeSubscriptionPolicy() + { + public boolean valid(EObject eObject, Adapter adapter) + { + return true; + } + }; + + boolean valid(EObject eObject, Adapter adapter); +} diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/CDONotification.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/CDONotification.java new file mode 100644 index 0000000000..d88f6cd894 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/CDONotification.java @@ -0,0 +1,26 @@ +/*************************************************************************** + * Copyright (c) 2004 - 2008 Eike Stepper, Germany. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Simon McDuff - initial API and implementation + **************************************************************************/ +package org.eclipse.emf.cdo; + +import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta; + +import org.eclipse.emf.common.notify.Notification; + +/** + * @author Simon McDuff + */ +public interface CDONotification extends Notification +{ + boolean hasNext(); + + public CDORevisionDelta getRevisionDelta(); + +} diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/CDOView.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/CDOView.java index 6cebdbe5f8..2fbcec0f1a 100644 --- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/CDOView.java +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/CDOView.java @@ -9,6 +9,8 @@ * Eike Stepper - initial API and implementation * Simon McDuff - https://bugs.eclipse.org/bugs/show_bug.cgi?id=201266 * Simon McDuff - https://bugs.eclipse.org/bugs/show_bug.cgi?id=201997 + * Simon McDuff - 233490: Change Subscription + * https://bugs.eclipse.org/bugs/show_bug.cgi?id=233490 **************************************************************************/ package org.eclipse.emf.cdo; @@ -60,6 +62,10 @@ public interface CDOView extends CDOProtocolView, INotifier public void setInvalidationNotificationsEnabled(boolean invalidationNotificationsEnabled); + public CDOChangeSubscriptionPolicy getChangeSubscriptionPolicy(); + + public void setChangeSubscriptionPolicy(CDOChangeSubscriptionPolicy changeSubscriptionPolicy); + public int getLoadRevisionCollectionChunkSize(); public void setLoadRevisionCollectionChunkSize(int loadRevisionCollectionChunkSize); diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDONotificationBuilder.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDONotificationBuilder.java new file mode 100644 index 0000000000..0b3cc3dbb6 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDONotificationBuilder.java @@ -0,0 +1,130 @@ +/*************************************************************************** + * Copyright (c) 2004 - 2008 Eike Stepper, Germany. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Simon McDuff - initial API and implementation + **************************************************************************/ +package org.eclipse.emf.internal.cdo; + +import org.eclipse.emf.cdo.common.revision.delta.CDOAddFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOClearFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOContainerFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDeltaVisitor; +import org.eclipse.emf.cdo.common.revision.delta.CDOListFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOMoveFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDORemoveFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOSetFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOUnsetFeatureDelta; +import org.eclipse.emf.cdo.util.CDOPackageRegistry; + +import org.eclipse.emf.internal.cdo.util.ModelUtil; + +import org.eclipse.emf.common.notify.impl.NotificationImpl; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.InternalEObject; + +/** + * @author Simon McDuff + */ +public class CDONotificationBuilder implements CDOFeatureDeltaVisitor +{ + private NotificationImpl notification = null; + + private CDORevisionDelta delta = null; + + private InternalEObject internalObject = null; + + private CDOPackageRegistry packageRegistry = null; + + public CDONotificationBuilder(CDOPackageRegistry packageRegistry) + { + this.packageRegistry = packageRegistry; + } + + synchronized public NotificationImpl buildNotification(InternalEObject internalObject, CDORevisionDelta delta) + { + notification = null; + this.internalObject = internalObject; + this.delta = delta; + delta.accept(this); + + return notification; + } + + protected void add(CDONotificationImpl newNotificaton) + { + newNotificaton.setRevisionDelta(delta); + + if (notification == null) + notification = newNotificaton; + else + notification.add(newNotificaton); + } + + public void visit(CDOMoveFeatureDelta delta) + { + EStructuralFeature eFeature = ModelUtil.getEFeature(delta.getFeature(), packageRegistry); + + int oldPosition = delta.getOldPosition(); + int newPosition = delta.getNewPosition(); + + add(new CDONotificationImpl(internalObject, NotificationImpl.MOVE, eFeature.getFeatureID(), + new Integer(oldPosition), null, newPosition)); + } + + public void visit(CDOAddFeatureDelta delta) + { + EStructuralFeature eFeature = ModelUtil.getEFeature(delta.getFeature(), packageRegistry); + + add(new CDONotificationImpl(internalObject, NotificationImpl.ADD, eFeature.getFeatureID(), null, delta.getValue(), + delta.getIndex())); + } + + public void visit(CDORemoveFeatureDelta delta) + { + EStructuralFeature eFeature = ModelUtil.getEFeature(delta.getFeature(), packageRegistry); + + add(new CDONotificationImpl(internalObject, NotificationImpl.REMOVE, eFeature.getFeatureID(), null, null, delta + .getIndex())); + } + + public void visit(CDOSetFeatureDelta delta) + { + EStructuralFeature eFeature = ModelUtil.getEFeature(delta.getFeature(), packageRegistry); + + add(new CDONotificationImpl(internalObject, NotificationImpl.SET, eFeature.getFeatureID(), null, delta.getValue())); + } + + public void visit(CDOUnsetFeatureDelta delta) + { + EStructuralFeature eFeature = ModelUtil.getEFeature(delta.getFeature(), packageRegistry); + + add(new CDONotificationImpl(internalObject, NotificationImpl.UNSET, eFeature.getFeatureID(), null, null)); + + } + + public void visit(CDOListFeatureDelta deltas) + { + for (CDOFeatureDelta delta : deltas.getListChanges()) + delta.accept(this); + } + + public void visit(CDOClearFeatureDelta delta) + { + EStructuralFeature eFeature = ModelUtil.getEFeature(delta.getFeature(), packageRegistry); + + add(new CDONotificationImpl(internalObject, NotificationImpl.SET, eFeature.getFeatureID(), null, null)); + } + + public void visit(CDOContainerFeatureDelta delta) + { + + } + +} diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDONotificationImpl.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDONotificationImpl.java new file mode 100644 index 0000000000..06dbe04db5 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDONotificationImpl.java @@ -0,0 +1,212 @@ +/*************************************************************************** + * Copyright (c) 2004 - 2008 Eike Stepper, Germany. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Simon McDuff - initial API and implementation + **************************************************************************/ +package org.eclipse.emf.internal.cdo; + +import org.eclipse.emf.cdo.CDONotification; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta; + +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.InternalEObject; +import org.eclipse.emf.ecore.impl.ENotificationImpl; + +/** + * @author Simon McDuff + */ +public class CDONotificationImpl extends ENotificationImpl implements CDONotification +{ + private CDORevisionDelta revisionDelta = null; + + + public CDONotificationImpl(InternalEObject notifier, int eventType, EStructuralFeature feature, Object oldValue, + Object newValue, boolean isSetChange) + { + super(notifier, eventType, feature, oldValue, newValue, isSetChange); + } + + public CDONotificationImpl(InternalEObject notifier, int eventType, EStructuralFeature feature, Object oldValue, + Object newValue, int position, boolean wasSet) + { + super(notifier, eventType, feature, oldValue, newValue, position, wasSet); + } + + public CDONotificationImpl(InternalEObject notifier, int eventType, EStructuralFeature feature, Object oldValue, + Object newValue, int position) + { + super(notifier, eventType, feature, oldValue, newValue, position); + } + + public CDONotificationImpl(InternalEObject notifier, int eventType, EStructuralFeature feature, Object oldValue, + Object newValue) + { + super(notifier, eventType, feature, oldValue, newValue); + } + + public CDONotificationImpl(InternalEObject notifier, int eventType, int featureID, boolean oldBooleanValue, + boolean newBooleanValue, boolean isSetChange) + { + super(notifier, eventType, featureID, oldBooleanValue, newBooleanValue, isSetChange); + } + + public CDONotificationImpl(InternalEObject notifier, int eventType, int featureID, boolean oldBooleanValue, + boolean newBooleanValue) + { + super(notifier, eventType, featureID, oldBooleanValue, newBooleanValue); + } + + public CDONotificationImpl(InternalEObject notifier, int eventType, int featureID, byte oldByteValue, + byte newByteValue, boolean isSetChange) + { + super(notifier, eventType, featureID, oldByteValue, newByteValue, isSetChange); + } + + public CDONotificationImpl(InternalEObject notifier, int eventType, int featureID, byte oldByteValue, + byte newByteValue) + { + super(notifier, eventType, featureID, oldByteValue, newByteValue); + } + + public CDONotificationImpl(InternalEObject notifier, int eventType, int featureID, char oldCharValue, + char newCharValue, boolean isSetChange) + { + super(notifier, eventType, featureID, oldCharValue, newCharValue, isSetChange); + } + + public CDONotificationImpl(InternalEObject notifier, int eventType, int featureID, char oldCharValue, + char newCharValue) + { + super(notifier, eventType, featureID, oldCharValue, newCharValue); + } + + public CDONotificationImpl(InternalEObject notifier, int eventType, int featureID, double oldDoubleValue, + double newDoubleValue, boolean isSetChange) + { + super(notifier, eventType, featureID, oldDoubleValue, newDoubleValue, isSetChange); + } + + public CDONotificationImpl(InternalEObject notifier, int eventType, int featureID, double oldDoubleValue, + double newDoubleValue) + { + super(notifier, eventType, featureID, oldDoubleValue, newDoubleValue); + } + + public CDONotificationImpl(InternalEObject notifier, int eventType, int featureID, float oldFloatValue, + float newFloatValue, boolean isSetChange) + { + super(notifier, eventType, featureID, oldFloatValue, newFloatValue, isSetChange); + } + + public CDONotificationImpl(InternalEObject notifier, int eventType, int featureID, float oldFloatValue, + float newFloatValue) + { + super(notifier, eventType, featureID, oldFloatValue, newFloatValue); + } + + public CDONotificationImpl(InternalEObject notifier, int eventType, int featureID, int oldIntValue, int newIntValue, + boolean isSetChange) + { + super(notifier, eventType, featureID, oldIntValue, newIntValue, isSetChange); + } + + public CDONotificationImpl(InternalEObject notifier, int eventType, int featureID, int oldIntValue, int newIntValue) + { + super(notifier, eventType, featureID, oldIntValue, newIntValue); + } + + public CDONotificationImpl(InternalEObject notifier, int eventType, int featureID, long oldLongValue, + long newLongValue, boolean isSetChange) + { + super(notifier, eventType, featureID, oldLongValue, newLongValue, isSetChange); + } + + public CDONotificationImpl(InternalEObject notifier, int eventType, int featureID, long oldLongValue, + long newLongValue) + { + super(notifier, eventType, featureID, oldLongValue, newLongValue); + } + + public CDONotificationImpl(InternalEObject notifier, int eventType, int featureID, Object oldValue, Object newValue, + boolean isSetChange) + { + super(notifier, eventType, featureID, oldValue, newValue, isSetChange); + } + + public CDONotificationImpl(InternalEObject notifier, int eventType, int featureID, Object oldValue, Object newValue, + int position, boolean wasSet) + { + super(notifier, eventType, featureID, oldValue, newValue, position, wasSet); + } + + public CDONotificationImpl(InternalEObject notifier, int eventType, int featureID, Object oldValue, Object newValue, + int position) + { + super(notifier, eventType, featureID, oldValue, newValue, position); + } + + public CDONotificationImpl(InternalEObject notifier, int eventType, int featureID, Object oldValue, Object newValue) + { + super(notifier, eventType, featureID, oldValue, newValue); + } + + public CDONotificationImpl(InternalEObject notifier, int eventType, int featureID, short oldShortValue, + short newShortValue, boolean isSetChange) + { + super(notifier, eventType, featureID, oldShortValue, newShortValue, isSetChange); + } + + public CDONotificationImpl(InternalEObject notifier, int eventType, int featureID, short oldShortValue, + short newShortValue) + { + super(notifier, eventType, featureID, oldShortValue, newShortValue); + } + private InternalCDOObject getCDOObject() + { + return (InternalCDOObject) getNotifier(); + } + @Override + public Object getNewValue() + { + Object object = super.getNewValue(); + + return adapt(object); + } + + @Override + public Object getOldValue() + { + return adapt(super.getOldValue()); + } + + public Object adapt(Object object) + { + if (object instanceof CDOID) + { + object = getCDOObject().cdoView().getObject((CDOID)object, true); + } + return object; + } + + public boolean hasNext() + { + return next != null; + } + + public CDORevisionDelta getRevisionDelta() + { + return revisionDelta; + } + + public void setRevisionDelta(CDORevisionDelta revisionDelta) + { + this.revisionDelta = revisionDelta; + } + +} diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOObjectImpl.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOObjectImpl.java index cc76599b02..83f66c71f4 100644 --- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOObjectImpl.java +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOObjectImpl.java @@ -7,6 +7,8 @@ * * Contributors: * Eike Stepper - initial API and implementation + * Simon McDuff - 233490: Change Subscription + * https://bugs.eclipse.org/bugs/show_bug.cgi?id=233490 **************************************************************************/ package org.eclipse.emf.internal.cdo; @@ -29,6 +31,7 @@ import org.eclipse.emf.internal.cdo.util.ModelUtil; import org.eclipse.net4j.util.ImplementationError; import org.eclipse.net4j.util.om.trace.ContextTracer; +import org.eclipse.emf.common.notify.Adapter; import org.eclipse.emf.common.util.BasicEMap; import org.eclipse.emf.common.util.ECollections; import org.eclipse.emf.common.util.EList; @@ -214,6 +217,14 @@ public class CDOObjectImpl extends EStoreEObjectImpl implements InternalCDOObjec } } } + if (eBasicAdapters() != null) + { + for (Adapter adapter : eBasicAdapters()) + { + cdoView().subscribe(this, adapter); + } + } + } @SuppressWarnings("unchecked") @@ -359,6 +370,37 @@ public class CDOObjectImpl extends EStoreEObjectImpl implements InternalCDOObjec { return eDynamicFeature(dynamicFeatureID); } + + @Override + public EList<Adapter> eAdapters() + { + if (eAdapters == null) + { + eAdapters = new EAdapterList<Adapter>(this) + { + private static final long serialVersionUID = 1L; + + @Override + protected void didAdd(int index, Adapter newObject) + { + if (!FSMUtil.isTransient(CDOObjectImpl.this)) + { + cdoView().subscribe(CDOObjectImpl.this, newObject); + } + } + + @Override + protected void didRemove(int index, Adapter oldObject) + { + if (!FSMUtil.isTransient(CDOObjectImpl.this)) + { + cdoView().unsubscribe(CDOObjectImpl.this, oldObject); + } + } + }; + } + return eAdapters; + } @Override protected FeatureMap createFeatureMap(EStructuralFeature eStructuralFeature) diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOSessionImpl.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOSessionImpl.java index 74da7fa192..b1825d37f9 100644 --- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOSessionImpl.java +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOSessionImpl.java @@ -10,6 +10,8 @@ * Simon McDuff - https://bugs.eclipse.org/bugs/show_bug.cgi?id=226778 * Simon McDuff - 230832: Make remote invalidation configurable * https://bugs.eclipse.org/bugs/show_bug.cgi?id=230832 + * Simon McDuff - 233490: Change Subscription + * https://bugs.eclipse.org/bugs/show_bug.cgi?id=233490 **************************************************************************/ package org.eclipse.emf.internal.cdo; @@ -28,6 +30,7 @@ import org.eclipse.emf.cdo.common.model.CDOClass; import org.eclipse.emf.cdo.common.model.CDOClassRef; import org.eclipse.emf.cdo.common.model.CDOPackage; import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta; import org.eclipse.emf.cdo.common.util.TransportException; import org.eclipse.emf.cdo.util.CDOPackageRegistry; import org.eclipse.emf.cdo.util.CDOUtil; @@ -555,19 +558,23 @@ public class CDOSessionImpl extends Container<CDOView> implements CDOSession, CD types.put(id, type); } - public void notifyInvalidation(long timeStamp, Set<CDOID> dirtyOIDs, CDOViewImpl excludedView) + public void notifyCommit(long timeStamp, Set<CDOID> dirtyOIDs, Collection<CDORevisionDelta> deltas, + CDOViewImpl excludedView) { - if (!isPassiveUpdateEnabled()) return; + if (isPassiveUpdateEnabled()) + { + notifyInvalidation(timeStamp, dirtyOIDs, excludedView); + } - forceNotifyInvalidation(timeStamp, dirtyOIDs, excludedView); + notifyChangeSubcription(deltas, excludedView); } - public void refresh(Set<CDOID> dirtyOIDs) + public void notifySync(Set<CDOID> dirtyOIDs) { - forceNotifyInvalidation(CDORevision.UNSPECIFIED_DATE, dirtyOIDs, null); + notifyInvalidation(CDORevision.UNSPECIFIED_DATE, dirtyOIDs, null); } - private void forceNotifyInvalidation(long timeStamp, Set<CDOID> dirtyOIDs, CDOViewImpl excludedView) + private void notifyInvalidation(long timeStamp, Set<CDOID> dirtyOIDs, CDOViewImpl excludedView) { dirtyOIDs = Collections.unmodifiableSet(dirtyOIDs); @@ -589,6 +596,26 @@ public class CDOSessionImpl extends Container<CDOView> implements CDOSession, CD fireInvalidationEvent(timeStamp, dirtyOIDs, excludedView); } + private void notifyChangeSubcription(Collection<CDORevisionDelta> deltas, CDOViewImpl excludedView) + { + if (deltas == null || deltas.size() <= 0) return; + + for (CDOViewImpl view : getViews()) + { + if (view != excludedView) + { + try + { + view.notifyChangeSubcription(deltas); + } + catch (RuntimeException ex) + { + OM.LOG.error(ex); + } + } + } + } + public void fireInvalidationEvent(long timeStamp, Set<CDOID> dirtyOIDs, CDOViewImpl excludedView) { fireEvent(new InvalidationEvent(excludedView, timeStamp, dirtyOIDs)); @@ -698,7 +725,8 @@ public class CDOSessionImpl extends Container<CDOView> implements CDOSession, CD channel = connector.openChannel(CDOProtocolConstants.PROTOCOL_NAME, this); } - OpenSessionRequest request = new OpenSessionRequest(channel, repositoryName, legacySupportEnabled); + OpenSessionRequest request = new OpenSessionRequest(channel, repositoryName, legacySupportEnabled, + passiveUpdateEnabled); OpenSessionResult result = request.send(); sessionID = result.getSessionID(); diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOTransactionImpl.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOTransactionImpl.java index 6e1cff523f..3b6bd62272 100644 --- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOTransactionImpl.java +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOTransactionImpl.java @@ -10,6 +10,8 @@ * Simon McDuff - https://bugs.eclipse.org/bugs/show_bug.cgi?id=201266 * Simon McDuff - https://bugs.eclipse.org/bugs/show_bug.cgi?id=233314 * Simon McDuff - https://bugs.eclipse.org/bugs/show_bug.cgi?id=215688 + * Simon McDuff - 233490: Change Subscription + * https://bugs.eclipse.org/bugs/show_bug.cgi?id=233490 **************************************************************************/ package org.eclipse.emf.internal.cdo; @@ -208,6 +210,8 @@ public class CDOTransactionImpl extends CDOViewImpl implements CDOTransaction { newPackages = analyzeNewPackages(); + Collection<CDORevisionDelta> deltas = getRevisionDeltas().values(); + for (CDOSavePointImpl itrSavePoint = lastSavePoint; itrSavePoint != null; itrSavePoint = itrSavePoint .getPreviousSavePoint()) { @@ -239,15 +243,19 @@ public class CDOTransactionImpl extends CDOViewImpl implements CDOTransaction ((InternalCDOPackage)newPackage).setPersistent(true); } + changeSubscriptionManager.notifyCommit(); + Map<CDOID, CDOObject> dirtyObjects = this.getDirtyObjects(); if (!dirtyObjects.isEmpty()) { - session.notifyInvalidation(result.getTimeStamp(), dirtyObjects.keySet(), this); + session.notifyCommit(result.getTimeStamp(), dirtyObjects.keySet(),deltas, this); } cleanUp(); Map<CDOIDTemp, CDOID> idMappings = result.getIDMappings(); + + fireEvent(new FinishedEvent(CDOTransactionFinishedEvent.Type.COMMITTED, idMappings)); } catch (RuntimeException ex) diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOViewImpl.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOViewImpl.java index 6a781b53ee..76b0ce733b 100644 --- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOViewImpl.java +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/CDOViewImpl.java @@ -13,9 +13,12 @@ * Simon McDuff - https://bugs.eclipse.org/bugs/show_bug.cgi?id=202064 * Simon McDuff - 230832: Make remote invalidation configurable * https://bugs.eclipse.org/bugs/show_bug.cgi?id=230832 + * Simon McDuff - 233490: Change Subscription + * https://bugs.eclipse.org/bugs/show_bug.cgi?id=233490 **************************************************************************/ package org.eclipse.emf.internal.cdo; +import org.eclipse.emf.cdo.CDOChangeSubscriptionPolicy; import org.eclipse.emf.cdo.CDOObject; import org.eclipse.emf.cdo.CDOState; import org.eclipse.emf.cdo.CDOView; @@ -29,6 +32,8 @@ import org.eclipse.emf.cdo.common.id.CDOIDProvider; import org.eclipse.emf.cdo.common.model.CDOClass; import org.eclipse.emf.cdo.common.model.CDOClassRef; import org.eclipse.emf.cdo.common.revision.CDORevisionResolver; +import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta; +import org.eclipse.emf.cdo.common.util.CDOException; import org.eclipse.emf.cdo.common.util.TransportException; import org.eclipse.emf.cdo.eresource.CDOResource; import org.eclipse.emf.cdo.eresource.EresourceFactory; @@ -40,6 +45,7 @@ import org.eclipse.emf.cdo.util.CDOUtil; import org.eclipse.emf.cdo.util.ReadOnlyException; import org.eclipse.emf.internal.cdo.bundle.OM; +import org.eclipse.emf.internal.cdo.protocol.ChangeSubscriptionRequest; import org.eclipse.emf.internal.cdo.protocol.ResourceIDRequest; import org.eclipse.emf.internal.cdo.protocol.ResourcePathRequest; import org.eclipse.emf.internal.cdo.query.CDOQueryImpl; @@ -55,6 +61,7 @@ import org.eclipse.net4j.util.transaction.TransactionException; import org.eclipse.emf.common.notify.Adapter; import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.common.notify.Notifier; +import org.eclipse.emf.common.notify.impl.NotificationImpl; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EClass; @@ -71,6 +78,8 @@ import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** @@ -103,6 +112,10 @@ public class CDOViewImpl extends org.eclipse.net4j.util.event.Notifier implement private InternalCDOObject lastLookupObject; + protected ChangeSubscriptionManager changeSubscriptionManager = new ChangeSubscriptionManager(); + + protected CDOChangeSubscriptionPolicy changeSubscriptionPolicy = CDOChangeSubscriptionPolicy.NONE; + public CDOViewImpl(int id, CDOSessionImpl session) { viewID = id; @@ -112,6 +125,187 @@ public class CDOViewImpl extends org.eclipse.net4j.util.event.Notifier implement objects = createObjectsMap(); } + public class ChangeSubscriptionManager + { + // List of persisted objects + private Map<InternalCDOObject, Integer> persistedObjects = new ConcurrentHashMap<InternalCDOObject, Integer>(); + + // List of new objects + private Map<InternalCDOObject, Integer> newObjects = new ConcurrentHashMap<InternalCDOObject, Integer>(); + + private boolean valid(InternalCDOObject internalCDOObject) + { + return !internalCDOObject.cdoID().isTemporary(); + } + + protected int getNumberOfValidAdapter(InternalCDOObject object) + { + int count = 0; + if (object.eNotificationRequired()) + { + for (Adapter adapter : object.eAdapters()) + { + if (changeSubscriptionPolicy.valid(object, adapter)) count++; + } + + } + return count; + } + + /* + * Register to the server all objects from the active list + */ + protected void notifyChangeSubcriptionPolicy() + { + synchronized (persistedObjects) + { + persistedObjects.clear(); + newObjects.clear(); + + List<CDOID> cdoIDs = new ArrayList<CDOID>(); + + if (changeSubscriptionPolicy != CDOChangeSubscriptionPolicy.NONE) + { + for (InternalCDOObject cdoObject : objects.values()) + { + int count = getNumberOfValidAdapter(cdoObject); + + if (count > 0) + { + cdoIDs.add(cdoObject.cdoID()); + boolean isValid = valid(cdoObject); + Map<InternalCDOObject, Integer> subscribersMap = isValid ? persistedObjects : newObjects; + subscribersMap.put(cdoObject, count); + } + } + } + request(cdoIDs, true, true); + } + } + + protected void request(List<CDOID> cdoIDs, boolean registered, boolean clear) + { + try + { + new ChangeSubscriptionRequest(getSession().getChannel(), getViewID(), cdoIDs, registered, clear).send(); + } + catch (Exception ex) + { + throw new TransactionException(ex); + } + } + + protected void notifyCommit() + { + synchronized (persistedObjects) + { + List<InternalCDOObject> objectToRemove = new ArrayList<InternalCDOObject>(); + for (Entry<InternalCDOObject, Integer> entry : newObjects.entrySet()) + { + if (valid(entry.getKey())) + { + subscribe(entry.getKey(), entry.getValue()); + objectToRemove.add(entry.getKey()); + } + } + for (InternalCDOObject internalCDOObject : objectToRemove) + newObjects.remove(internalCDOObject); + } + } + + protected boolean isSubscribe(InternalCDOObject eObject) + { + return (persistedObjects.get(eObject) != null); + } + + private void subscribe(EObject eObject, Adapter adapter, int adjust) + { + synchronized (persistedObjects) + { + if (!getChangeSubscriptionPolicy().valid(eObject, adapter)) return; + + subscribe(eObject, adjust); + } + } + + private void subscribe(EObject eObject, int adjust) + { + synchronized (persistedObjects) + { + + InternalCDOObject internalCDOObject = FSMUtil.adapt(eObject, CDOViewImpl.this); + + if (internalCDOObject.cdoView() != CDOViewImpl.this) + { + throw new CDOException("Object " + internalCDOObject + " doesn`t belong to this view."); + } + + boolean isValid = valid(internalCDOObject); + + Map<InternalCDOObject, Integer> subscribersMap = isValid ? persistedObjects : newObjects; + + Integer count = subscribersMap.get(internalCDOObject); + + if (count == null) + { + // Cannot adjust negative value + if (adjust < 0) throw new IllegalStateException(); + + count = 0; + + // Notification need to be enable to send correct value to the server + if (isValid && getChangeSubscriptionPolicy() != CDOChangeSubscriptionPolicy.NONE) + { + List<CDOID> cdoIDs = new ArrayList<CDOID>(); + cdoIDs.add(internalCDOObject.cdoID()); + request(cdoIDs, true, false); + } + } + + count += adjust; + + // Look if objects need to be unsubscribe + if (count <= 0) + { + subscribersMap.remove(internalCDOObject); + + // Notification need to be enable to send correct value to the server + if (isValid && getChangeSubscriptionPolicy() != CDOChangeSubscriptionPolicy.NONE) + { + List<CDOID> cdoIDs = new ArrayList<CDOID>(); + cdoIDs.add(internalCDOObject.cdoID()); + request(cdoIDs, false, false); + } + } + else + { + subscribersMap.put(internalCDOObject, count); + } + + } + } + + public void subscribe(EObject eObject, Adapter adapter) + { + subscribe(eObject, adapter, 1); + } + + public void unsubscribe(EObject eObject, Adapter adapter) + { + subscribe(eObject, adapter, -1); + } + } + + public void subscribe(EObject eObject, Adapter adapter) + { + changeSubscriptionManager.subscribe(eObject, adapter); + } + + public void unsubscribe(EObject eObject, Adapter adapter) + { + changeSubscriptionManager.unsubscribe(eObject, adapter); + } + protected ConcurrentMap<CDOID, InternalCDOObject> createObjectsMap() { return new ReferenceValueMap.Weak<CDOID, InternalCDOObject>(); @@ -172,6 +366,21 @@ public class CDOViewImpl extends org.eclipse.net4j.util.event.Notifier implement this.invalidationNotificationsEnabled = invalidationNotificationsEnabled; } + public CDOChangeSubscriptionPolicy getChangeSubscriptionPolicy() + { + return this.changeSubscriptionPolicy; + } + + public void setChangeSubscriptionPolicy(CDOChangeSubscriptionPolicy notificationsEnabled) + { + if (this.changeSubscriptionPolicy != notificationsEnabled) + { + changeSubscriptionPolicy = notificationsEnabled; + + changeSubscriptionManager.notifyChangeSubcriptionPolicy(); + } + } + public int getLoadRevisionCollectionChunkSize() { return loadRevisionCollectionChunkSize; @@ -325,6 +534,8 @@ public class CDOViewImpl extends org.eclipse.net4j.util.event.Notifier implement public InternalCDOObject getObject(CDOID id, boolean loadOnDemand) { + if (id == null || id.isNull()) return null; + synchronized (objects) { if (id.equals(lastLookupID)) @@ -635,6 +846,24 @@ public class CDOViewImpl extends org.eclipse.net4j.util.event.Notifier implement } } + public void notifyChangeSubcription(Collection<CDORevisionDelta> deltas) + { + if (deltas != null && getChangeSubscriptionPolicy() != CDOChangeSubscriptionPolicy.NONE) + { + CDONotificationBuilder builder = new CDONotificationBuilder(getSession().getPackageRegistry()); + + for (CDORevisionDelta delta : deltas) + { + InternalCDOObject object = objects.get(delta.getID()); + if (object != null && object.eNotificationRequired() && changeSubscriptionManager.isSubscribe(object)) + { + NotificationImpl notification = builder.buildNotification(object, delta); + if (notification != null) notification.dispatch(); + } + } + } + } + public int reload(CDOObject... objects) { Collection<InternalCDOObject> internalObjects; diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/protocol/ChangeSubscriptionRequest.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/protocol/ChangeSubscriptionRequest.java new file mode 100644 index 0000000000..ec44b509bf --- /dev/null +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/protocol/ChangeSubscriptionRequest.java @@ -0,0 +1,76 @@ +/*************************************************************************** + * Copyright (c) 2004 - 2008 Eike Stepper, Germany. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Simon McDuff - initial API and implementation + * 230832: Make remote invalidation configurable + * https://bugs.eclipse.org/bugs/show_bug.cgi?id=230832 + **************************************************************************/ +package org.eclipse.emf.internal.cdo.protocol; + +import org.eclipse.emf.cdo.common.CDOProtocolConstants; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.id.CDOIDUtil; + +import org.eclipse.emf.internal.cdo.bundle.OM; + +import org.eclipse.net4j.channel.IChannel; +import org.eclipse.net4j.util.io.ExtendedDataInputStream; +import org.eclipse.net4j.util.io.ExtendedDataOutputStream; +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import java.io.IOException; +import java.util.List; + +/** + * @author Simon McDuff + */ +public class ChangeSubscriptionRequest extends CDOClientRequest<Boolean> +{ + private static final ContextTracer PROTOCOL = new ContextTracer(OM.DEBUG_PROTOCOL, ChangeSubscriptionRequest.class); + + private int viewID; + + private List<CDOID> cdoIDs; + + private boolean registered; + + private boolean clear; + + public ChangeSubscriptionRequest(IChannel channel, int viewID, List<CDOID> cdoIDs, boolean registered, boolean clear) + { + super(channel); + this.viewID = viewID; + this.cdoIDs = cdoIDs; + this.registered = registered; + this.clear = clear; + } + + @Override + protected short getSignalID() + { + return CDOProtocolConstants.SIGNAL_CHANGE_SUBSCRIPTION; + } + + @Override + protected void requesting(ExtendedDataOutputStream out) throws IOException + { + if (PROTOCOL.isEnabled()) PROTOCOL.trace("View " + viewID + " subscribing to " + cdoIDs.size()); + + out.writeInt(viewID); + out.writeBoolean(clear); + out.writeInt(registered ? cdoIDs.size() : -cdoIDs.size()); + for (CDOID id : cdoIDs) + CDOIDUtil.write(out, id); + } + + @Override + protected Boolean confirming(ExtendedDataInputStream in) throws IOException + { + return in.readBoolean(); + } +} diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/protocol/InvalidationIndication.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/protocol/InvalidationIndication.java index a44290171d..391669d776 100644 --- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/protocol/InvalidationIndication.java +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/protocol/InvalidationIndication.java @@ -7,12 +7,16 @@ * * Contributors: * Eike Stepper - initial API and implementation + * Simon McDuff - 233490: Change Subscription + * https://bugs.eclipse.org/bugs/show_bug.cgi?id=233490 **************************************************************************/ package org.eclipse.emf.internal.cdo.protocol; import org.eclipse.emf.cdo.common.CDOProtocolConstants; import org.eclipse.emf.cdo.common.id.CDOID; import org.eclipse.emf.cdo.common.id.CDOIDUtil; +import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta; +import org.eclipse.emf.cdo.internal.common.revision.delta.CDORevisionDeltaImpl; import org.eclipse.emf.internal.cdo.CDOSessionImpl; import org.eclipse.emf.internal.cdo.bundle.OM; @@ -22,7 +26,9 @@ import org.eclipse.net4j.util.io.ExtendedDataInputStream; import org.eclipse.net4j.util.om.trace.ContextTracer; import java.io.IOException; +import java.util.ArrayList; import java.util.HashSet; +import java.util.List; import java.util.Set; /** @@ -64,8 +70,20 @@ public class InvalidationIndication extends Indication CDOID dirtyOID = CDOIDUtil.read(in, session); dirtyOIDs.add(dirtyOID); } - - session.notifyInvalidation(timeStamp, dirtyOIDs, null); + + size = in.readInt(); + if (PROTOCOL.isEnabled()) + { + PROTOCOL.format("Reading {0} Deltas", size); + } + + List<CDORevisionDelta> deltas = new ArrayList<CDORevisionDelta>(); + for (int i = 0; i < size; i++) + { + deltas.add(new CDORevisionDeltaImpl(in, getSession().getPackageManager())); + } + + session.notifyCommit(timeStamp, dirtyOIDs, deltas, null); } protected CDOSessionImpl getSession() diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/protocol/OpenSessionRequest.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/protocol/OpenSessionRequest.java index a980178055..c5c2157de4 100644 --- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/protocol/OpenSessionRequest.java +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/protocol/OpenSessionRequest.java @@ -7,6 +7,8 @@ * * Contributors: * Eike Stepper - initial API and implementation + * Simon McDuff - 230832: Make remote invalidation configurable + * https://bugs.eclipse.org/bugs/show_bug.cgi?id=230832 **************************************************************************/ package org.eclipse.emf.internal.cdo.protocol; @@ -38,11 +40,15 @@ public class OpenSessionRequest extends RequestWithConfirmation<OpenSessionResul private boolean legacySupportEnabled; - public OpenSessionRequest(IChannel channel, String repositoryName, boolean legacySupportEnabled) + private boolean passiveUpdateEnabled; + + public OpenSessionRequest(IChannel channel, String repositoryName, boolean legacySupportEnabled, + boolean passiveUpdateEnabled) { super(channel); this.repositoryName = repositoryName; this.legacySupportEnabled = legacySupportEnabled; + this.passiveUpdateEnabled = passiveUpdateEnabled; } @Override @@ -65,6 +71,12 @@ public class OpenSessionRequest extends RequestWithConfirmation<OpenSessionResul PROTOCOL.format("Writing legacySupportEnabled: {0}", legacySupportEnabled); } out.writeBoolean(legacySupportEnabled); + + if (PROTOCOL.isEnabled()) + { + PROTOCOL.format("Writing passiveUpdateEnabled: {0}", passiveUpdateEnabled); + } + out.writeBoolean(passiveUpdateEnabled); } @Override diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/protocol/PassiveUpdateRequest.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/protocol/PassiveUpdateRequest.java index 4654883ff9..132d06f724 100644 --- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/protocol/PassiveUpdateRequest.java +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/protocol/PassiveUpdateRequest.java @@ -44,7 +44,7 @@ public class PassiveUpdateRequest extends SyncRevisionRequest @Override protected short getSignalID() { - return CDOProtocolConstants.SIGNAL_AUTOMATIC_REFRESH; + return CDOProtocolConstants.SIGNAL_PASSIVE_UPDATE; } @Override diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/protocol/SyncRevisionRequest.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/protocol/SyncRevisionRequest.java index f62aecd8b7..230991e7bc 100644 --- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/protocol/SyncRevisionRequest.java +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/protocol/SyncRevisionRequest.java @@ -99,7 +99,7 @@ public class SyncRevisionRequest extends CDOClientRequest<Set<CDOID>> if (PROTOCOL.isEnabled()) PROTOCOL.trace("Synchronization received " + size + " dirty objects"); - cdoSession.refresh(listofDirtyObjects); + cdoSession.notifySync(listofDirtyObjects); return listofDirtyObjects; } |