You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by md...@apache.org on 2011/09/02 18:35:57 UTC
svn commit: r1164616 - in /jackrabbit/sandbox/jackrabbit-mk:
jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/
jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/
jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/...
Author: mduerig
Date: Fri Sep 2 16:35:56 2011
New Revision: 1164616
URL: http://svn.apache.org/viewvc?rev=1164616&view=rev
Log:
Microkernel based Jackrabbit prototype (WIP)
observation: use ExecutorService for change polling instead of per session thread
Modified:
jackrabbit/sandbox/jackrabbit-mk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceImpl.java
jackrabbit/sandbox/jackrabbit-mk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceManager.java
jackrabbit/sandbox/jackrabbit-mk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/AbstractJCR2SPITest.java
jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/AbstractReadableRepositoryService.java
jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/AbstractRepositoryService.java
jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/logging/RepositoryServiceLogger.java
jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi/src/main/java/org/apache/jackrabbit/spi/RepositoryService.java
jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi/src/main/java/org/apache/jackrabbit/spi/Subscription.java
Modified: jackrabbit/sandbox/jackrabbit-mk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-mk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceImpl.java?rev=1164616&r1=1164615&r2=1164616&view=diff
==============================================================================
--- jackrabbit/sandbox/jackrabbit-mk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceImpl.java (original)
+++ jackrabbit/sandbox/jackrabbit-mk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceImpl.java Fri Sep 2 16:35:56 2011
@@ -79,6 +79,7 @@ import javax.jcr.version.Version;
import javax.jcr.version.VersionException;
import java.io.IOException;
import java.io.InputStream;
+import java.util.concurrent.ExecutorService;
/**
* <code>WorkspaceImpl</code>...
Modified: jackrabbit/sandbox/jackrabbit-mk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-mk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceManager.java?rev=1164616&r1=1164615&r2=1164616&view=diff
==============================================================================
--- jackrabbit/sandbox/jackrabbit-mk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceManager.java (original)
+++ jackrabbit/sandbox/jackrabbit-mk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceManager.java Fri Sep 2 16:35:56 2011
@@ -16,32 +16,6 @@
*/
package org.apache.jackrabbit.jcr2spi;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.Semaphore;
-
-import javax.jcr.AccessDeniedException;
-import javax.jcr.InvalidItemStateException;
-import javax.jcr.ItemExistsException;
-import javax.jcr.ItemNotFoundException;
-import javax.jcr.MergeException;
-import javax.jcr.NamespaceException;
-import javax.jcr.NamespaceRegistry;
-import javax.jcr.NoSuchWorkspaceException;
-import javax.jcr.PathNotFoundException;
-import javax.jcr.ReferentialIntegrityException;
-import javax.jcr.RepositoryException;
-import javax.jcr.UnsupportedRepositoryOperationException;
-import javax.jcr.lock.LockException;
-import javax.jcr.nodetype.ConstraintViolationException;
-import javax.jcr.nodetype.NoSuchNodeTypeException;
-import javax.jcr.query.InvalidQueryException;
-import javax.jcr.version.VersionException;
-
import org.apache.jackrabbit.jcr2spi.config.CacheBehaviour;
import org.apache.jackrabbit.jcr2spi.hierarchy.HierarchyEventListener;
import org.apache.jackrabbit.jcr2spi.hierarchy.HierarchyManager;
@@ -116,6 +90,22 @@ import org.apache.jackrabbit.spi.commons
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import javax.jcr.NamespaceRegistry;
+import javax.jcr.RepositoryException;
+import javax.jcr.UnsupportedRepositoryOperationException;
+import javax.jcr.lock.LockException;
+import javax.jcr.query.InvalidQueryException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.Semaphore;
+
/**
* <code>WorkspaceManager</code>...
*/
@@ -131,7 +121,7 @@ public class WorkspaceManager implements
private final HierarchyManager hierarchyManager;
private final CacheBehaviour cacheBehaviour;
- private final boolean observationSupported;
+ private final ExecutorService changePollingExecutor;
private final int pollTimeout;
private final IdFactory idFactory;
@@ -152,12 +142,7 @@ public class WorkspaceManager implements
* It is also <code>null</code> if {@link CacheBehaviour#INVALIDATE} is
* configured and no event listeners have been registered.
*/
- private Thread changeFeed;
-
- /**
- * Flag that indicates that the changeFeed thread should be disposed.
- */
- private volatile boolean disposeChangeFeed;
+ private Future<Object> changeFeed; // No bottom type in Java. Use object and discard return value
/**
* List of event listener that are set on this WorkspaceManager to get
@@ -176,13 +161,16 @@ public class WorkspaceManager implements
private ItemInfoCache cache;
public WorkspaceManager(RepositoryService service, SessionInfo sessionInfo, CacheBehaviour cacheBehaviour,
- int pollTimeout, boolean observationSupported) throws RepositoryException {
+ int pollTimeout, boolean observationSupported)
+ throws RepositoryException {
this.service = service;
this.sessionInfo = sessionInfo;
this.cacheBehaviour = cacheBehaviour;
- this.observationSupported = observationSupported;
this.pollTimeout = pollTimeout;
+ changePollingExecutor = observationSupported
+ ? service.getChangePollingExecutor()
+ : null;
nameFactory = service.getNameFactory();
pathFactory = service.getPathFactory();
@@ -353,8 +341,8 @@ public class WorkspaceManager implements
* @throws RepositoryException if the listener cannot be registered.
*/
public void addEventListener(InternalEventListener listener) throws RepositoryException {
- if (changeFeed == null) {
- changeFeed = createChangeFeed(pollTimeout, observationSupported);
+ if (changeFeed == null && changePollingExecutor != null) {
+ changeFeed = changePollingExecutor.submit(new ChangePolling(pollTimeout));
}
synchronized (listeners) {
@@ -525,26 +513,6 @@ public class WorkspaceManager implements
return new ItemDefinitionProviderImpl(entProvider, service, sessionInfo);
}
- /**
- * Creates a background thread which polls for external changes on the
- * RepositoryService.
- *
- * @param pollTimeout the polling timeout in milliseconds.
- * @param enableObservation if observation should be enabled.
- * @return the background polling thread or <code>null</code> if the underlying
- * <code>RepositoryService</code> does not support observation.
- */
- private Thread createChangeFeed(int pollTimeout, boolean enableObservation) {
- Thread t = null;
- if (enableObservation) {
- t = new Thread(new ChangePolling(pollTimeout));
- t.setName("Change Polling");
- t.setDaemon(true);
- t.start();
- }
- return t;
- }
-
//-----------------------------------------------------< wsp management >---
/**
@@ -631,9 +599,7 @@ public class WorkspaceManager implements
}
try {
if (changeFeed != null) {
- disposeChangeFeed = true;
- changeFeed.interrupt();
- changeFeed.join();
+ changeFeed.cancel(true);
}
hierarchyManager.dispose();
if (subscription != null) {
@@ -1089,7 +1055,7 @@ public class WorkspaceManager implements
/**
* Implements the polling for changes on the repository service.
*/
- private final class ChangePolling implements Runnable {
+ private final class ChangePolling implements Callable<Object> {
/**
* The polling timeout in milliseconds.
@@ -1106,44 +1072,35 @@ public class WorkspaceManager implements
}
@Override
- public void run() {
+ public Object call() throws Exception {
String wspName = sessionInfo.getWorkspaceName();
- while (!Thread.interrupted() && !disposeChangeFeed) {
- try {
- InternalEventListener[] iel;
- Subscription subscription;
- synchronized (listeners) {
- while (WorkspaceManager.this.subscription == null) {
- listeners.wait();
- }
- iel = listeners.toArray(new InternalEventListener[listeners.size()]);
- subscription = WorkspaceManager.this.subscription;
+ try {
+ InternalEventListener[] iel;
+ Subscription subscription;
+ synchronized (listeners) {
+ while (WorkspaceManager.this.subscription == null) {
+ listeners.wait();
}
+ iel = listeners.toArray(new InternalEventListener[listeners.size()]);
+ subscription = WorkspaceManager.this.subscription;
+ }
- log.debug("calling getEvents() (Workspace={})", wspName);
- EventBundle[] bundles = service.getEvents(subscription, pollTimeout);
- log.debug("returned from getEvents() (Workspace={})", wspName);
- // check if thread had been interrupted while
- // getting events
- if (Thread.interrupted() || disposeChangeFeed) {
- log.debug("Thread interrupted, terminating...");
- break;
- }
- if (bundles.length > 0) {
- onEventReceived(bundles, iel);
- }
- } catch (UnsupportedRepositoryOperationException e) {
- log.error("SPI implementation does not support observation: " + e);
- // terminate
- break;
- } catch (RepositoryException e) {
- log.info("Workspace=" + wspName + ": Exception while retrieving event bundles: " + e);
- log.debug("Dump:", e);
- } catch (InterruptedException e) {
- // terminate
- break;
+ log.debug("calling getEvents() (Workspace={})", wspName);
+ EventBundle[] bundles = service.getEvents(subscription, pollTimeout);
+ log.debug("returned from getEvents() (Workspace={})", wspName);
+
+ if (!Thread.interrupted() && bundles.length > 0) {
+ onEventReceived(bundles, iel);
}
+ changePollingExecutor.submit(this);
+ } catch (UnsupportedRepositoryOperationException e) {
+ log.error("SPI implementation does not support observation: " + e);
+ } catch (RepositoryException e) {
+ log.info("Workspace=" + wspName + ": Exception while retrieving event bundles: " + e);
+ log.debug("Dump:", e);
}
+ return null;
}
+
}
}
Modified: jackrabbit/sandbox/jackrabbit-mk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/AbstractJCR2SPITest.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-mk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/AbstractJCR2SPITest.java?rev=1164616&r1=1164615&r2=1164616&view=diff
==============================================================================
--- jackrabbit/sandbox/jackrabbit-mk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/AbstractJCR2SPITest.java (original)
+++ jackrabbit/sandbox/jackrabbit-mk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/AbstractJCR2SPITest.java Fri Sep 2 16:35:56 2011
@@ -25,6 +25,7 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.ExecutorService;
import javax.jcr.Credentials;
import javax.jcr.ItemNotFoundException;
@@ -523,9 +524,13 @@ public abstract class AbstractJCR2SPITes
values);
}
-
//--------------------------------------------------------< Observation >---
+ @Override
+ public ExecutorService getChangePollingExecutor() throws RepositoryException {
+ return repositoryService.getChangePollingExecutor();
+ }
+
public EventFilter createEventFilter(SessionInfo sessionInfo, int eventTypes, Path absPath,
boolean isDeep, String[] uuid, Name[] nodeTypeName, boolean noLocal) throws RepositoryException {
Modified: jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/AbstractReadableRepositoryService.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/AbstractReadableRepositoryService.java?rev=1164616&r1=1164615&r2=1164616&view=diff
==============================================================================
--- jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/AbstractReadableRepositoryService.java (original)
+++ jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/AbstractReadableRepositoryService.java Fri Sep 2 16:35:56 2011
@@ -25,6 +25,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ExecutorService;
import javax.jcr.Credentials;
import javax.jcr.ItemNotFoundException;
Modified: jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/AbstractRepositoryService.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/AbstractRepositoryService.java?rev=1164616&r1=1164615&r2=1164616&view=diff
==============================================================================
--- jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/AbstractRepositoryService.java (original)
+++ jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/AbstractRepositoryService.java Fri Sep 2 16:35:56 2011
@@ -16,39 +16,6 @@
*/
package org.apache.jackrabbit.spi.commons;
-import java.io.InputStream;
-import java.io.Reader;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-import javax.jcr.AccessDeniedException;
-import javax.jcr.Credentials;
-import javax.jcr.GuestCredentials;
-import javax.jcr.InvalidItemStateException;
-import javax.jcr.ItemExistsException;
-import javax.jcr.ItemNotFoundException;
-import javax.jcr.LoginException;
-import javax.jcr.MergeException;
-import javax.jcr.NamespaceException;
-import javax.jcr.NoSuchWorkspaceException;
-import javax.jcr.PathNotFoundException;
-import javax.jcr.PropertyType;
-import javax.jcr.ReferentialIntegrityException;
-import javax.jcr.Repository;
-import javax.jcr.RepositoryException;
-import javax.jcr.SimpleCredentials;
-import javax.jcr.UnsupportedRepositoryOperationException;
-import javax.jcr.ValueFormatException;
-import javax.jcr.lock.LockException;
-import javax.jcr.nodetype.ConstraintViolationException;
-import javax.jcr.nodetype.InvalidNodeTypeDefinitionException;
-import javax.jcr.nodetype.NoSuchNodeTypeException;
-import javax.jcr.nodetype.NodeTypeExistsException;
-import javax.jcr.query.InvalidQueryException;
-import javax.jcr.version.VersionException;
-
import org.apache.jackrabbit.commons.cnd.CompactNodeTypeDefReader;
import org.apache.jackrabbit.commons.cnd.ParseException;
import org.apache.jackrabbit.spi.Batch;
@@ -81,6 +48,40 @@ import org.apache.jackrabbit.spi.commons
import org.apache.jackrabbit.spi.commons.nodetype.QDefinitionBuilderFactory;
import org.apache.jackrabbit.spi.commons.value.QValueFactoryImpl;
+import javax.jcr.AccessDeniedException;
+import javax.jcr.Credentials;
+import javax.jcr.GuestCredentials;
+import javax.jcr.InvalidItemStateException;
+import javax.jcr.ItemExistsException;
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.LoginException;
+import javax.jcr.MergeException;
+import javax.jcr.NamespaceException;
+import javax.jcr.NoSuchWorkspaceException;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.PropertyType;
+import javax.jcr.ReferentialIntegrityException;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.SimpleCredentials;
+import javax.jcr.UnsupportedRepositoryOperationException;
+import javax.jcr.ValueFormatException;
+import javax.jcr.lock.LockException;
+import javax.jcr.nodetype.ConstraintViolationException;
+import javax.jcr.nodetype.InvalidNodeTypeDefinitionException;
+import javax.jcr.nodetype.NoSuchNodeTypeException;
+import javax.jcr.nodetype.NodeTypeExistsException;
+import javax.jcr.query.InvalidQueryException;
+import javax.jcr.version.VersionException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
/**
* <code>AbstractRepositoryService</code> provides an abstract base class for
* repository service implementations. This class provides default
@@ -114,6 +115,9 @@ public abstract class AbstractRepository
*/
protected QNodeDefinition rootNodeDefinition;
+ /** Executor service for change polling */
+ private ExecutorService changePollingExecutor;
+
/**
* @return {@link IdFactoryImpl#getInstance()}.
* @throws RepositoryException if an error occurs.
@@ -387,7 +391,9 @@ public abstract class AbstractRepository
* This default implementation does nothing.
*/
public void dispose(SessionInfo sessionInfo) throws RepositoryException {
- // do nothing
+ if (changePollingExecutor != null) {
+ changePollingExecutor.shutdown();
+ }
}
//-----------------------------< node types >-------------------------------
@@ -881,4 +887,17 @@ public abstract class AbstractRepository
throw new UnsupportedRepositoryOperationException();
}
+ //------------------------------------------< observation >---
+
+ /**
+ * @return an instance of a single thread executor
+ */
+ @Override
+ public ExecutorService getChangePollingExecutor() {
+ if (changePollingExecutor == null) {
+ changePollingExecutor = Executors.newSingleThreadExecutor();
+ }
+ return changePollingExecutor;
+ }
+
}
Modified: jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/logging/RepositoryServiceLogger.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/logging/RepositoryServiceLogger.java?rev=1164616&r1=1164615&r2=1164616&view=diff
==============================================================================
--- jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/logging/RepositoryServiceLogger.java (original)
+++ jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/logging/RepositoryServiceLogger.java Fri Sep 2 16:35:56 2011
@@ -19,6 +19,7 @@ package org.apache.jackrabbit.spi.common
import java.io.InputStream;
import java.util.Iterator;
import java.util.Map;
+import java.util.concurrent.ExecutorService;
import javax.jcr.Credentials;
import javax.jcr.RepositoryException;
@@ -586,6 +587,15 @@ public class RepositoryServiceLogger ext
qnodeTypeName, noLocal});
}
+ @Override
+ public ExecutorService getChangePollingExecutor() throws RepositoryException {
+ return (ExecutorService) execute(new Callable() {
+ public Object call() throws RepositoryException {
+ return service.getChangePollingExecutor();
+ }
+ }, "getChangePollingExecutor()", new Object[]{});
+ }
+
public Subscription createSubscription(final SessionInfo sessionInfo, final EventFilter[] filters)
throws RepositoryException {
Modified: jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi/src/main/java/org/apache/jackrabbit/spi/RepositoryService.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi/src/main/java/org/apache/jackrabbit/spi/RepositoryService.java?rev=1164616&r1=1164615&r2=1164616&view=diff
==============================================================================
--- jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi/src/main/java/org/apache/jackrabbit/spi/RepositoryService.java (original)
+++ jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi/src/main/java/org/apache/jackrabbit/spi/RepositoryService.java Fri Sep 2 16:35:56 2011
@@ -42,6 +42,8 @@ import javax.jcr.version.VersionExceptio
import java.io.InputStream;
import java.util.Iterator;
import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
/**
* The <code>RepositoryService</code> interface defines methods used to
@@ -920,6 +922,14 @@ public interface RepositoryService {
throws RepositoryException;
//--------------------------------------------------------< Observation >---
+
+ /**
+ * Return an executor service to use for change polling. If <code>null</code>
+ * observation is disabled.
+ * @return
+ */
+ public ExecutorService getChangePollingExecutor() throws RepositoryException;
+
/**
* Creates an event filter. If the repository supports observation, the
* filter created is based on the parameters available in {@link
Modified: jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi/src/main/java/org/apache/jackrabbit/spi/Subscription.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi/src/main/java/org/apache/jackrabbit/spi/Subscription.java?rev=1164616&r1=1164615&r2=1164616&view=diff
==============================================================================
--- jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi/src/main/java/org/apache/jackrabbit/spi/Subscription.java (original)
+++ jackrabbit/sandbox/jackrabbit-mk/jackrabbit-spi/src/main/java/org/apache/jackrabbit/spi/Subscription.java Fri Sep 2 16:35:56 2011
@@ -16,6 +16,8 @@
*/
package org.apache.jackrabbit.spi;
+import java.util.concurrent.ExecutorService;
+
/**
* <code>Subscription</code> defines a marker interface for an event
* subscription. An implementation will likely keep information in this object