public void runExtractor(Message msg) {
        try(final Timer.Context ignored = completeTimer.time()) {
            final String field;
            try (final Timer.Context ignored2 = conditionTimer.time()) {
                // We can only work on Strings.
                if (!(msg.getField(sourceField) instanceof String)) {
                    conditionMissesCounter.inc();
                    return;
                }

                field = (String) msg.getField(sourceField);

                // Decide if to extract at all.
                if (conditionType.equals(ConditionType.STRING)) {
                    if (field.contains(conditionValue)) {
                        conditionHitsCounter.inc();
                    } else {
                        conditionMissesCounter.inc();
                        return;
                    }
                } else if (conditionType.equals(ConditionType.REGEX)) {
                    if (regexConditionPattern.matcher(field).find()) {
                        conditionHitsCounter.inc();
                    } else {
                        conditionMissesCounter.inc();
                        return;
                    }
                }
            }

            try (final Timer.Context ignored2 = executionTimer.time()) {
                Result[] results;
                try {
                    results = run(field);
                } catch (ExtractorException e) {
                    final String error = "Could not apply extractor <" + getTitle() + " (" + getId() + ")>";
                    msg.addProcessingError(new Message.ProcessingError(
                            ProcessingFailureCause.ExtractorException, error, ExceptionUtils.getRootCauseMessage(e)));
                    return;
                }

                if (results == null || results.length == 0 || Arrays.stream(results).anyMatch(result -> result.getValue() == null)) {
                    return;
                } else if (results.length == 1 && results[0].target == null) {
                    // results[0].target is null if this extractor cannot produce multiple fields use targetField in that case
                    msg.addField(targetField, results[0].getValue());
                } else {
                    for (final Result result : results) {
                        msg.addField(result.getTarget(), result.getValue());
                    }
                }

                // Remove original from message?
                if (cursorStrategy.equals(CursorStrategy.CUT) && !targetField.equals(sourceField) && !Message.RESERVED_FIELDS.contains(sourceField) && results[0].beginIndex != -1) {
                    final StringBuilder sb = new StringBuilder(field);

                    final List<Result> reverseList = Arrays.stream(results)
                            .sorted(Comparator.<Result>comparingInt(result -> result.endIndex).reversed())
                            .collect(Collectors.toList());

                    // remove all from reverse so that the indices still match
                    for (final Result result : reverseList) {
                        sb.delete(result.getBeginIndex(), result.getEndIndex());
                    }

                    final String builtString = sb.toString();
                    final String finalResult = builtString.trim().isEmpty() ? "fullyCutByExtractor" : builtString;

                    msg.removeField(sourceField);
                    // TODO don't add an empty field back, or rather don't add fullyCutByExtractor
                    msg.addField(sourceField, finalResult);
                }

                runConverters(msg);
            }
        }
    }
@Test
    public void testCursorStrategyCopy() throws Exception {
        final TestExtractor extractor = new TestExtractor.Builder()
                .cursorStrategy(COPY)
                .sourceField("msg")
                .callback(new Callable<Result[]>() {
                    @Override
                    public Result[] call() throws Exception {
                        return new Result[] {
                                new Result("the", 0, 3)
                        };
                    }
                })
                .build();

        final Message msg = createMessage("message");
        msg.addField("msg", "the hello");

        extractor.runExtractor(msg);

        // With the copy strategy, the source field should not be modified.
        assertThat(msg.getField("msg")).isEqualTo("the hello");
    }