private void pushChannel(
      IPushChannel channel, IPreferences prefs, Notification notification, boolean ignoreFrequency)
      throws PushException, RenderException {

    IPushChannelPreferences cPrefs = prefs.getChannelPrefs().get(channel.getId());
    if (cPrefs == null) {
      cPrefs = channel.newDefaultPreferences();
      if (cPrefs == null) {
        // don't flood user after he configures this channel
        throw new PushException("channel not configured for user", false);
      }
    }

    if (!channel.getNotificationTypes().contains(notification.getType())) {
      // channel not applicable for type
      throw new PushException("channel not applicable for type " + notification.getType(), false);
    }

    Dispatch dispatch = _dispatchService.create(notification, prefs, cPrefs);

    if (!channel.isConfigured(dispatch.getParams())) {
      // don't flood user after he configures this channel
      throw new PushException("channel not configured for user", false);
    }

    if (!ignoreFrequency && !Frequency.INSTANT.equals(cPrefs.getFrequency())) {
      // temporary as other dispatcher will handle this
      throw new PushException("channel not configured for this frequency", true);
    }

    channel.push(dispatch);
  }
  private void recordPushAttempt(Notification notification, @Nonnull PushResultMessage rm) {

    if (rm.getResult() == PushResult.SUCCESS) {
      notification.setPushState(PushState.PUSHED);
      notification.setPushDate(new Date());
      notification.setPushErrorMessage(rm.getMessage());
    } else {
      int errorCount = notification.recordPushError(rm.getMessage());

      if (errorCount > _maxErrorCount || rm.getResult() == PushResult.PERSISTENT_ERROR) {
        notification.setPushState(PushState.UNDELIVERABLE);
        notification.setPushDate(new Date());
      } else {
        notification.setPushState(PushState.QUEUED);
        notification.setPushDate(new Date(System.currentTimeMillis() + waitAfter(errorCount)));
      }
    }

    _notificationDAO.update(notification);
  }
  @Nonnull
  private PushResultMessage push(@Nonnull Notification notification, boolean ignoreFrequency) {
    IPreferences prefs;

    final String unknownChannel = notification.getParams().get(INotifyService.NOTIFY_UNKNOWN);
    if (unknownChannel != null) {
      prefs = new Preferences().setUserId(notification.getUserId());
    } else {
      prefs = _preferencesDAO.getPreferences(notification.getUserId());
    }

    if (prefs == null) {
      log.warn("can't push to unknown user " + notification.getUserId());
      return PushResultMessage.persistent("unknown user " + notification.getUserId());
    }

    Set<String> successChannels = Sets.newHashSet();
    Map<String, String> temporaryChannels = Maps.newHashMap();
    Map<String, String> persistentChannels = Maps.newHashMap();

    Iterable<IPushChannel> channels =
        Iterables.filter(
            _pushChannels,
            new Predicate<IPushChannel>() {

              @Override
              public boolean apply(IPushChannel channel) {
                return unknownChannel == null || channel.getId().equals(unknownChannel);
              }
            });

    for (IPushChannel channel : channels) {
      try {
        pushChannel(channel, prefs, notification, ignoreFrequency);
        successChannels.add(channel.getId());
      } catch (PushException e) {
        if (e.isTemporaryError()) {
          temporaryChannels.put(channel.getId(), e.getMessage());
        } else {
          persistentChannels.put(channel.getId(), e.getMessage());
        }
        if (_errorListener != null) {
          _errorListener.error(notification, channel, e);
        } else {
          log.info(
              "failed to deliver notification "
                  + notification
                  + " on channel "
                  + channel.getId()
                  + ": "
                  + ExceptionUtils.getAllMessages(e));
        }
      } catch (RenderException e) {
        log.error("failed to render notification " + notification, e);
        temporaryChannels.put(channel.getId(), ExceptionUtils.getAllMessages(e));
      }
    }

    if (successChannels.size() > 0) {
      return PushResultMessage.success("channels: " + successChannels);
    } else if (temporaryChannels.size() > 0) {
      return PushResultMessage.temporary("temporary error, channels: " + temporaryChannels);
    } else if (persistentChannels.size() > 0) {
      return PushResultMessage.persistent("persistent error, channels: " + persistentChannels);
    } else {
      return PushResultMessage.temporary("no allowed channels available");
    }
  }