public List<Stream> match(Message message) {
        final Set<Stream> result = Sets.newHashSet();
        final Set<String> blackList = Sets.newHashSet();

        for (final Rule rule : rulesList) {
            if (blackList.contains(rule.getStreamId())) {
                continue;
            }

            final StreamRule streamRule = rule.getStreamRule();
            final StreamRuleType streamRuleType = streamRule.getType();
            final Stream.MatchingType matchingType = rule.getMatchingType();
            if (!ruleTypesNotNeedingFieldPresence.contains(streamRuleType)
                    && !message.hasField(streamRule.getField())) {
                if (matchingType == Stream.MatchingType.AND) {
                    result.remove(rule.getStream());
                    // blacklist stream because it can't match anymore
                    blackList.add(rule.getStreamId());
                }

                continue;
            }

            final Stream stream;
            if (streamRuleType != StreamRuleType.REGEX) {
                stream = rule.match(message);
            } else {
                stream = rule.matchWithTimeOut(message, streamProcessingTimeout, TimeUnit.MILLISECONDS);
            }

            if (stream == null) {
                if (matchingType == Stream.MatchingType.AND) {
                    result.remove(rule.getStream());
                    // blacklist stream because it can't match anymore
                    blackList.add(rule.getStreamId());
                }
            } else {
                result.add(stream);
                if (matchingType == Stream.MatchingType.OR) {
                    // blacklist stream because it is already matched
                    blackList.add(rule.getStreamId());
                }
            }
        }

        final Stream defaultStream = defaultStreamProvider.get();
        boolean alreadyRemovedDefaultStream = false;
        for (Stream stream : result) {
            if (stream.getRemoveMatchesFromDefaultStream()) {
                if (alreadyRemovedDefaultStream || message.removeStream(defaultStream)) {
                    alreadyRemovedDefaultStream = true;
                    if (LOG.isTraceEnabled()) {
                        LOG.trace("Successfully removed default stream <{}> from message <{}>", defaultStream.getId(), message.getId());
                    }
                } else {
                    // A previously executed message processor (or Illuminate) has likely already removed the
                    // default stream from the message. Now, the message has matched a stream in the Graylog
                    // MessageFilterChain, and the matching stream is also set to remove the default stream.
                    // This is usually from user-defined stream rules, and is generally not a problem.
                    cannotRemoveDefaultMeter.inc();
                    if (LOG.isTraceEnabled()) {
                        LOG.trace("Couldn't remove default stream <{}> from message <{}>", defaultStream.getId(), message.getId());
                    }
                }
            }
        }

        return ImmutableList.copyOf(result);
    }
@Test
    public void issue1396() throws Exception {
        final StreamMock stream = getStreamMock("GitHub issue #1396");
        stream.setMatchingType(Stream.MatchingType.AND);

        final StreamRuleMock rule1 = new StreamRuleMock(ImmutableMap.<String, Object>builder()
                .put("_id", new ObjectId())
                .put("field", "custom1")
                .put("value", "value1")
                .put("type", StreamRuleType.EXACT.toInteger())
                .put("inverted", false)
                .put("stream_id", stream.getId())
                .build()
        );
        final StreamRuleMock rule2 = new StreamRuleMock(ImmutableMap.<String, Object>builder()
                .put("_id", new ObjectId())
                .put("field", "custom2")
                .put("value", "value2")
                .put("type", StreamRuleType.EXACT.toInteger())
                .put("inverted", false)
                .put("stream_id", stream.getId())
                .build()
        );

        stream.setStreamRules(Lists.newArrayList(rule1, rule2));

        final StreamRouterEngine engine = newEngine(Lists.newArrayList(stream));

        final Message message1 = getMessage();
        message1.addFields(ImmutableMap.of("custom1", "value1"));

        assertTrue("Message without \"custom2\" should not match conditions", engine.match(message1).isEmpty());

        final Message message2 = getMessage();
        message2.addFields(ImmutableMap.of(
                        "custom1", "value1",
                        "custom2", "value2"
                )
        );

        assertEquals("Message with \"custom1\" and \"custom2\" should match conditions",
                Lists.newArrayList(stream), engine.match(message2));
    }