Listeners: What are store listeners and how to work with them?

In order execute a custom action before or after a document is stored the IDocumentStoreListener needs to be implemented:

public interface IDocumentStoreListener {
  /**
   * Invoked before the store request is sent to the server.
   * @param key The key.
   * @param entityInstance The entity instance.
   * @param metadata The metadata.
   * @param original The original document that was loaded from the server
   * @return
   *
   * Whatever the entity instance was modified and requires us re-serialize it.
   * Returning true would force re-serialization of the entity, returning false would
   * mean that any changes to the entityInstance would be ignored in the current SaveChanges call.
   */
  boolean beforeStore(String key, Object entityInstance, RavenJObject metadata, RavenJObject original);

  /**
   * Invoked after the store request is sent to the server.
   * @param key The key.
   * @param entityInstance The entity instance.
   * @param metadata The metadata
   */
  void afterStore(String key, Object entityInstance, RavenJObject metadata);
}

Example

To prevent anyone from adding documents with a certain key, you can create FilterForbiddenKeysDocumentListener. In result the document with the specified forbidden id will not be stored.

public static class FilterForbiddenKeysDocumentListener implements IDocumentStoreListener {
  private static final List<String> FORBIDDEN_KEYS = Arrays.asList("system");

  @Override
  public boolean beforeStore(String key, Object entityInstance, RavenJObject metadata, RavenJObject original) {
    return FORBIDDEN_KEYS.contains(key.toLowerCase());
  }

  @Override
  public void afterStore(String key, Object entityInstance, RavenJObject metadata) {
    //empty by design
  }
}