public void removeNotificationListener(final MetadataNotificationListener listener) {
    Validate.notNull(listener, "Metadata notification listener required");

    if (listener instanceof MetadataService && listener.equals(metadataService)) {
      metadataService = null;
      return;
    }

    listeners.remove(listener);
  }
  public void notifyDownstream(final String upstreamDependency) {
    try {
      metadataLogger.startEvent();

      if (metadataService != null) {
        // First dispatch the fine-grained, instance-specific
        // dependencies.
        Set<String> notifiedDownstreams = new HashSet<String>();
        for (final String downstream : getDownstream(upstreamDependency)) {
          if (metadataLogger.getTraceLevel() > 0) {
            metadataLogger.log(upstreamDependency + " -> " + downstream);
          }
          // No need to ensure upstreamDependency is different from
          // downstream, as that's taken care of in the
          // isValidDependency() method
          try {
            final String responsibleClass =
                MetadataIdentificationUtils.getMetadataClass(downstream);
            metadataLogger.startTimer(responsibleClass);
            metadataService.notify(upstreamDependency, downstream);
          } finally {
            metadataLogger.stopTimer();
          }
          notifiedDownstreams.add(downstream);
        }

        // Next dispatch the coarse-grained, class-specific
        // dependencies.
        // We only do it if the upstream is not class specific, as
        // otherwise we'd have handled class-specific dispatch in
        // previous loop
        if (!MetadataIdentificationUtils.isIdentifyingClass(upstreamDependency)) {
          final String asClass = MetadataIdentificationUtils.getMetadataClassId(upstreamDependency);
          for (final String downstream : getDownstream(asClass)) {
            // We don't notify a downstream if it had a direct
            // instance-specific dependency and was already notified
            // in previous loop
            // We also don't notify if upstream is the same as
            // downstream, as it doesn't make sense to notify
            // yourself of an event
            // (such a condition is only possible if an instance
            // registered to receive class-specific notifications
            // and that instance
            // caused an event to fire)
            if (!notifiedDownstreams.contains(downstream)
                && !upstreamDependency.equals(downstream)) {
              if (metadataLogger.getTraceLevel() > 0) {
                metadataLogger.log(upstreamDependency + " -> " + downstream + " [via class]");
              }
              try {
                final String responsibleClass =
                    MetadataIdentificationUtils.getMetadataClass(downstream);
                metadataLogger.startTimer(responsibleClass);
                metadataService.notify(upstreamDependency, downstream);
              } finally {
                metadataLogger.stopTimer();
              }
            }
          }
        }

        notifiedDownstreams = null;
      }

      // Finally dispatch the general-purpose additional listeners
      for (final MetadataNotificationListener listener : listeners) {
        if (metadataLogger.getTraceLevel() > 1) {
          metadataLogger.log(
              upstreamDependency
                  + " -> "
                  + upstreamDependency
                  + " ["
                  + listener.getClass().getSimpleName()
                  + "]");
        }
        try {
          final String responsibleClass = listener.getClass().getName();
          metadataLogger.startTimer(responsibleClass);
          listener.notify(upstreamDependency, null);
        } finally {
          metadataLogger.stopTimer();
        }
      }
    } finally {
      metadataLogger.stopEvent();
    }
  }