    @Override
    public void onTrigger(final ProcessContext context, final ProcessSession session) throws ProcessException {
        final String tableName = context.getProperty(TABLE_NAME).getValue();
        final String initialTimeRange = context.getProperty(INITIAL_TIMERANGE).getValue();
        final String filterExpression = context.getProperty(FILTER_EXPRESSION).getValue();

        List<String> authorizations = getAuthorizations(context, null);

        final HBaseClientService hBaseClientService = context.getProperty(HBASE_CLIENT_SERVICE).asControllerService(HBaseClientService.class);

        // if the table was changed then remove any previous state
        if (previousTable != null && !tableName.equals(previousTable)) {
            try {
                context.getStateManager().clear(Scope.CLUSTER);
            } catch (final IOException ioe) {
                getLogger().warn("Failed to clear Cluster State", ioe);
            }
            previousTable = tableName;
        }

        try {
            final Charset charset = Charset.forName(context.getProperty(CHARSET).getValue());
            final RowSerializer serializer = new JsonRowSerializer(charset);

            this.lastResult = getState(context.getStateManager());
            final long defaultMinTime = (initialTimeRange.equals(NONE.getValue()) ? 0L : System.currentTimeMillis());
            final long minTime = (lastResult == null ? defaultMinTime : lastResult.getTimestamp());

            final Map<String, Set<String>> cellsMatchingTimestamp = new HashMap<>();

            final AtomicReference<Long> rowsPulledHolder = new AtomicReference<>(0L);
            final AtomicReference<Long> latestTimestampHolder = new AtomicReference<>(minTime);

            hBaseClientService.scan(tableName, columns, filterExpression, minTime, authorizations, (rowKey, resultCells) -> {

                final String rowKeyString = new String(rowKey, StandardCharsets.UTF_8);

                // check if latest cell timestamp is equal to our cutoff.
                // if any of the cells have a timestamp later than our cutoff, then we
                // want the row. But if the cell with the latest timestamp is equal to
                // our cutoff, then we want to check if that's one of the cells that
                // we have already seen.
                long latestCellTimestamp = 0L;
                for (final ResultCell cell : resultCells) {
                    if (cell.getTimestamp() > latestCellTimestamp) {
                        latestCellTimestamp = cell.getTimestamp();
                    }
                }

                // we've already seen this.
                if (latestCellTimestamp < minTime) {
                    getLogger().debug("latest cell timestamp for row {} is {}, which is earlier than the minimum time of {}",
                            new Object[] {rowKeyString, latestCellTimestamp, minTime});
                    return;
                }

                if (latestCellTimestamp == minTime) {
                    // latest cell timestamp is equal to our minimum time. Check if all cells that have
                    // that timestamp are in our list of previously seen cells.
                    boolean allSeen = true;
                    for (final ResultCell cell : resultCells) {
                        if (cell.getTimestamp() == latestCellTimestamp) {
                            if (lastResult == null || !lastResult.contains(cell)) {
                                allSeen = false;
                                break;
                            }
                        }
                    }

                    if (allSeen) {
                        // we have already seen all of the cells for this row. We do not want to
                        // include this cell in our output.
                        getLogger().debug("all cells for row {} have already been seen", new Object[] { rowKeyString });
                        return;
                    }
                }

                // If the latest timestamp of the cell is later than the latest timestamp we have already seen,
                // we want to keep track of the cells that match this timestamp so that the next time we scan,
                // we can ignore these cells.
                if (latestCellTimestamp >= latestTimestampHolder.get()) {
                    // new timestamp, so clear all of the 'matching cells'
                    if (latestCellTimestamp > latestTimestampHolder.get()) {
                        latestTimestampHolder.set(latestCellTimestamp);
                        cellsMatchingTimestamp.clear();
                    }

                    for (final ResultCell cell : resultCells) {
                        final long ts = cell.getTimestamp();
                        if (ts == latestCellTimestamp) {
                            final byte[] rowValue = Arrays.copyOfRange(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength() + cell.getRowOffset());
                            final byte[] cellValue = Arrays.copyOfRange(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength() + cell.getValueOffset());

                            final String rowHash = new String(rowValue, StandardCharsets.UTF_8);
                            Set<String> cellHashes = cellsMatchingTimestamp.get(rowHash);
                            if (cellHashes == null) {
                                cellHashes = new HashSet<>();
                                cellsMatchingTimestamp.put(rowHash, cellHashes);
                            }
                            cellHashes.add(new String(cellValue, StandardCharsets.UTF_8));
                        }
                    }
                }

                // write the row to a new FlowFile.
                FlowFile flowFile = session.create();
                flowFile = session.write(flowFile, out -> serializer.serialize(rowKey, resultCells, out));

                final Map<String, String> attributes = new HashMap<>();
                attributes.put("hbase.table", tableName);
                attributes.put("mime.type", "application/json");
                flowFile = session.putAllAttributes(flowFile, attributes);

                session.getProvenanceReporter().receive(flowFile, hBaseClientService.toTransitUri(tableName, rowKeyString));
                session.transfer(flowFile, REL_SUCCESS);
                getLogger().debug("Received {} from HBase with row key {}", new Object[]{flowFile, rowKeyString});

                // we could potentially have a huge number of rows. If we get to 500, go ahead and commit the
                // session so that we can avoid buffering tons of FlowFiles without ever sending any out.
                long rowsPulled = rowsPulledHolder.get();
                rowsPulledHolder.set(++rowsPulled);

                if (++rowsPulled % getBatchSize() == 0) {
                    session.commit();
                }
            });

            final ScanResult scanResults = new ScanResult(latestTimestampHolder.get(), cellsMatchingTimestamp);

            // Commit session before we replace the lastResult; if session commit fails, we want
            // to pull these records again.
            session.commit();
            if (lastResult == null || scanResults.getTimestamp() > lastResult.getTimestamp()) {
                lastResult = scanResults;
            } else if (scanResults.getTimestamp() == lastResult.getTimestamp()) {
                final Map<String, Set<String>> combinedResults = new HashMap<>(scanResults.getMatchingCells());

                // copy the results of result.getMatchingCells() to combinedResults.
                // do a deep copy because the Set may be modified below.
                for (final Map.Entry<String, Set<String>> entry : scanResults.getMatchingCells().entrySet()) {
                    combinedResults.put(entry.getKey(), new HashSet<>(entry.getValue()));
                }

                // combined the results from 'lastResult'
                for (final Map.Entry<String, Set<String>> entry : lastResult.getMatchingCells().entrySet()) {
                    final Set<String> existing = combinedResults.get(entry.getKey());
                    if (existing == null) {
                        combinedResults.put(entry.getKey(), new HashSet<>(entry.getValue()));
                    } else {
                        existing.addAll(entry.getValue());
                    }
                }
                final ScanResult scanResult = new ScanResult(scanResults.getTimestamp(), combinedResults);
                lastResult = scanResult;
            }

            // save state using the framework's state manager
            storeState(lastResult, context.getStateManager());
        } catch (final IOException e) {
            getLogger().error("Failed to receive data from HBase due to {}", e);
            session.rollback();
        } finally {
            // if we failed, we want to yield so that we don't hammer hbase. If we succeed, then we have
            // pulled all of the records, so we want to wait a bit before hitting hbase again anyway.
            context.yield();
        }
    }

    @Override
    public void onTrigger(final ProcessContext context, final ProcessSession session) throws ProcessException {
        final String tableName = context.getProperty(TABLE_NAME).getValue();
        final String initialTimeRange = context.getProperty(INITIAL_TIMERANGE).getValue();
        final String filterExpression = context.getProperty(FILTER_EXPRESSION).getValue();
        final HBaseClientService hBaseClientService = context.getProperty(HBASE_CLIENT_SERVICE).asControllerService(HBaseClientService.class);

        // if the table was changed then remove any previous state
        if (previousTable != null && !tableName.equals(previousTable)) {
            try {
                context.getStateManager().clear(Scope.CLUSTER);
            } catch (final IOException ioe) {
                getLogger().warn("Failed to clear Cluster State", ioe);
            }
            previousTable = tableName;
        }

        try {
            final Charset charset = Charset.forName(context.getProperty(CHARSET).getValue());
            final RowSerializer serializer = new JsonRowSerializer(charset);

            this.lastResult = getState(context.getStateManager());
            final long defaultMinTime = (initialTimeRange.equals(NONE.getValue()) ? 0L : System.currentTimeMillis());
            final long minTime = (lastResult == null ? defaultMinTime : lastResult.getTimestamp());

            final Map<String, Set<String>> cellsMatchingTimestamp = new HashMap<>();

            final AtomicReference<Long> rowsPulledHolder = new AtomicReference<>(0L);
            final AtomicReference<Long> latestTimestampHolder = new AtomicReference<>(minTime);

            hBaseClientService.scan(tableName, columns, filterExpression, minTime, new ResultHandler() {
                @Override
                public void handle(final byte[] rowKey, final ResultCell[] resultCells) {

                    final String rowKeyString = new String(rowKey, StandardCharsets.UTF_8);

                    // check if latest cell timestamp is equal to our cutoff.
                    // if any of the cells have a timestamp later than our cutoff, then we
                    // want the row. But if the cell with the latest timestamp is equal to
                    // our cutoff, then we want to check if that's one of the cells that
                    // we have already seen.
                    long latestCellTimestamp = 0L;
                    for (final ResultCell cell : resultCells) {
                        if (cell.getTimestamp() > latestCellTimestamp) {
                            latestCellTimestamp = cell.getTimestamp();
                        }
                    }

                    // we've already seen this.
                    if (latestCellTimestamp < minTime) {
                        getLogger().debug("latest cell timestamp for row {} is {}, which is earlier than the minimum time of {}",
                                new Object[] {rowKeyString, latestCellTimestamp, minTime});
                        return;
                    }

                    if (latestCellTimestamp == minTime) {
                        // latest cell timestamp is equal to our minimum time. Check if all cells that have
                        // that timestamp are in our list of previously seen cells.
                        boolean allSeen = true;
                        for (final ResultCell cell : resultCells) {
                            if (cell.getTimestamp() == latestCellTimestamp) {
                                if (lastResult == null || !lastResult.contains(cell)) {
                                    allSeen = false;
                                    break;
                                }
                            }
                        }

                        if (allSeen) {
                            // we have already seen all of the cells for this row. We do not want to
                            // include this cell in our output.
                            getLogger().debug("all cells for row {} have already been seen", new Object[] { rowKeyString });
                            return;
                        }
                    }

                    // If the latest timestamp of the cell is later than the latest timestamp we have already seen,
                    // we want to keep track of the cells that match this timestamp so that the next time we scan,
                    // we can ignore these cells.
                    if (latestCellTimestamp >= latestTimestampHolder.get()) {
                        // new timestamp, so clear all of the 'matching cells'
                        if (latestCellTimestamp > latestTimestampHolder.get()) {
                            latestTimestampHolder.set(latestCellTimestamp);
                            cellsMatchingTimestamp.clear();
                        }

                        for (final ResultCell cell : resultCells) {
                            final long ts = cell.getTimestamp();
                            if (ts == latestCellTimestamp) {
                                final byte[] rowValue = Arrays.copyOfRange(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength() + cell.getRowOffset());
                                final byte[] cellValue = Arrays.copyOfRange(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength() + cell.getValueOffset());

                                final String rowHash = new String(rowValue, StandardCharsets.UTF_8);
                                Set<String> cellHashes = cellsMatchingTimestamp.get(rowHash);
                                if (cellHashes == null) {
                                    cellHashes = new HashSet<>();
                                    cellsMatchingTimestamp.put(rowHash, cellHashes);
                                }
                                cellHashes.add(new String(cellValue, StandardCharsets.UTF_8));
                            }
                        }
                    }

                    // write the row to a new FlowFile.
                    FlowFile flowFile = session.create();
                    flowFile = session.write(flowFile, new OutputStreamCallback() {
                        @Override
                        public void process(final OutputStream out) throws IOException {
                            serializer.serialize(rowKey, resultCells, out);
                        }
                    });

                    final Map<String, String> attributes = new HashMap<>();
                    attributes.put("hbase.table", tableName);
                    attributes.put("mime.type", "application/json");
                    flowFile = session.putAllAttributes(flowFile, attributes);

                    session.getProvenanceReporter().receive(flowFile, hBaseClientService.toTransitUri(tableName, rowKeyString));
                    session.transfer(flowFile, REL_SUCCESS);
                    getLogger().debug("Received {} from HBase with row key {}", new Object[]{flowFile, rowKeyString});

                    // we could potentially have a huge number of rows. If we get to 500, go ahead and commit the
                    // session so that we can avoid buffering tons of FlowFiles without ever sending any out.
                    long rowsPulled = rowsPulledHolder.get();
                    rowsPulledHolder.set(++rowsPulled);

                    if (++rowsPulled % getBatchSize() == 0) {
                        session.commit();
                    }
                }
            });

            final ScanResult scanResults = new ScanResult(latestTimestampHolder.get(), cellsMatchingTimestamp);

            // Commit session before we replace the lastResult; if session commit fails, we want
            // to pull these records again.
            session.commit();
            if (lastResult == null || scanResults.getTimestamp() > lastResult.getTimestamp()) {
                lastResult = scanResults;
            } else if (scanResults.getTimestamp() == lastResult.getTimestamp()) {
                final Map<String, Set<String>> combinedResults = new HashMap<>(scanResults.getMatchingCells());

                // copy the results of result.getMatchingCells() to combinedResults.
                // do a deep copy because the Set may be modified below.
                for (final Map.Entry<String, Set<String>> entry : scanResults.getMatchingCells().entrySet()) {
                    combinedResults.put(entry.getKey(), new HashSet<>(entry.getValue()));
                }

                // combined the results from 'lastResult'
                for (final Map.Entry<String, Set<String>> entry : lastResult.getMatchingCells().entrySet()) {
                    final Set<String> existing = combinedResults.get(entry.getKey());
                    if (existing == null) {
                        combinedResults.put(entry.getKey(), new HashSet<>(entry.getValue()));
                    } else {
                        existing.addAll(entry.getValue());
                    }
                }
                final ScanResult scanResult = new ScanResult(scanResults.getTimestamp(), combinedResults);
                lastResult = scanResult;
            }

            // save state using the framework's state manager
            storeState(lastResult, context.getStateManager());
        } catch (final IOException e) {
            getLogger().error("Failed to receive data from HBase due to {}", e);
            session.rollback();
        } finally {
            // if we failed, we want to yield so that we don't hammer hbase. If we succeed, then we have
            // pulled all of the records, so we want to wait a bit before hitting hbase again anyway.
            context.yield();
        }
    }

Replace Anonymous With Lambda org.apache.nifi.hbase.GetHBase.onTrigger.scan.new ResultHandler with (rowKey, resultCells) -> {final String rowKeyString=new String(rowKey,StandardCharsets.UTF_8); in method public onTrigger(context ProcessContext, session ProcessSession) : void from class org.apache.nifi.hbase.GetHBase


    @Override
    public Map<NodeConnectionState, List<NodeIdentifier>> getConnectionStates() {
        final Map<NodeConnectionState, List<NodeIdentifier>> connectionStates = new HashMap<>();
        for (final Map.Entry<NodeIdentifier, NodeConnectionStatus> entry : nodeStatuses.entrySet()) {
            final NodeConnectionState state = entry.getValue().getState();
            final List<NodeIdentifier> nodeIds = connectionStates.computeIfAbsent(state, s -> new ArrayList<>());
            nodeIds.add(entry.getKey());
        }

        return connectionStates;
    }

    @Override
    public Map<NodeConnectionState, List<NodeIdentifier>> getConnectionStates() {
        final Map<NodeConnectionState, List<NodeIdentifier>> connectionStates = new HashMap<>();
        for (final Map.Entry<NodeIdentifier, NodeConnectionStatus> entry : nodeStatuses.entrySet()) {
            final NodeConnectionState state = entry.getValue().getState();
            final List<NodeIdentifier> nodeIds = connectionStates.computeIfAbsent(state, s -> new ArrayList<NodeIdentifier>());
            nodeIds.add(entry.getKey());
        }

        return connectionStates;
    }

Replace Generic With Diamond ArrayList<NodeIdentifier> with ArrayList<> in method public getConnectionStates() : Map<NodeConnectionState,List<NodeIdentifier>> from class org.apache.nifi.cluster.coordination.node.NodeClusterCoordinator


    @Override
    public void onTrigger(ProcessContext context, ProcessSession session) throws ProcessException {
        OkHttpClient okHttpClient = okHttpClientAtomicReference.get();

        FlowFile requestFlowFile = session.get();

        // Checking to see if the property to put the body of the response in an attribute was set
        boolean putToAttribute = context.getProperty(PROP_PUT_OUTPUT_IN_ATTRIBUTE).isSet();
        if (requestFlowFile == null) {
            if(context.hasNonLoopConnection()){
                return;
            }

            String request = context.getProperty(PROP_METHOD).evaluateAttributeExpressions().getValue().toUpperCase();
            if ("POST".equals(request) || "PUT".equals(request) || "PATCH".equals(request)) {
                return;
            } else if (putToAttribute) {
                requestFlowFile = session.create();
            }
        }

        // Setting some initial variables
        final int maxAttributeSize = context.getProperty(PROP_PUT_ATTRIBUTE_MAX_LENGTH).asInteger();
        final ComponentLog logger = getLogger();

        // log ETag cache metrics
        final boolean eTagEnabled = context.getProperty(PROP_USE_ETAG).asBoolean();
        if(eTagEnabled && logger.isDebugEnabled()) {
            final Cache cache = okHttpClient.cache();
            logger.debug("OkHttp ETag cache metrics :: Request Count: {} | Network Count: {} | Hit Count: {}",
                    new Object[] {cache.requestCount(), cache.networkCount(), cache.hitCount()});
        }

        // Every request/response cycle has a unique transaction id which will be stored as a flowfile attribute.
        final UUID txId = UUID.randomUUID();

        FlowFile responseFlowFile = null;
        try {
            // read the url property from the context
            final String urlstr = trimToEmpty(context.getProperty(PROP_URL).evaluateAttributeExpressions(requestFlowFile).getValue());
            final URL url = new URL(urlstr);

            Request httpRequest = configureRequest(context, session, requestFlowFile, url);

            // log request
            logRequest(logger, httpRequest);

            // emit send provenance event if successfully sent to the server
            if (httpRequest.body() != null) {
                session.getProvenanceReporter().send(requestFlowFile, url.toExternalForm(), true);
            }

            final long startNanos = System.nanoTime();

            try (Response responseHttp = okHttpClient.newCall(httpRequest).execute()) {
                // output the raw response headers (DEBUG level only)
                logResponse(logger, url, responseHttp);

                // store the status code and message
                int statusCode = responseHttp.code();
                String statusMessage = responseHttp.message();

                if (statusCode == 0) {
                    throw new IllegalStateException("Status code unknown, connection hasn't been attempted.");
                }

                // Create a map of the status attributes that are always written to the request and response FlowFiles
                Map<String, String> statusAttributes = new HashMap<>();
                statusAttributes.put(STATUS_CODE, String.valueOf(statusCode));
                statusAttributes.put(STATUS_MESSAGE, statusMessage);
                statusAttributes.put(REQUEST_URL, url.toExternalForm());
                statusAttributes.put(TRANSACTION_ID, txId.toString());

                if (requestFlowFile != null) {
                    requestFlowFile = session.putAllAttributes(requestFlowFile, statusAttributes);
                }

                // If the property to add the response headers to the request flowfile is true then add them
                if (context.getProperty(PROP_ADD_HEADERS_TO_REQUEST).asBoolean() && requestFlowFile != null) {
                    // write the response headers as attributes
                    // this will overwrite any existing flowfile attributes
                    requestFlowFile = session.putAllAttributes(requestFlowFile, convertAttributesFromHeaders(url, responseHttp));
                }

                boolean outputBodyToRequestAttribute = (!isSuccess(statusCode) || putToAttribute) && requestFlowFile != null;
                boolean outputBodyToResponseContent = (isSuccess(statusCode) && !putToAttribute) || context.getProperty(PROP_OUTPUT_RESPONSE_REGARDLESS).asBoolean();
                ResponseBody responseBody = responseHttp.body();
                boolean bodyExists = responseBody != null;

                InputStream responseBodyStream = null;
                SoftLimitBoundedByteArrayOutputStream outputStreamToRequestAttribute = null;
                TeeInputStream teeInputStream = null;
                try {
                    responseBodyStream = bodyExists ? responseBody.byteStream() : null;
                    if (responseBodyStream != null && outputBodyToRequestAttribute && outputBodyToResponseContent) {
                        outputStreamToRequestAttribute = new SoftLimitBoundedByteArrayOutputStream(maxAttributeSize);
                        teeInputStream = new TeeInputStream(responseBodyStream, outputStreamToRequestAttribute);
                    }

                    if (outputBodyToResponseContent) {
                        /*
                         * If successful and putting to response flowfile, store the response body as the flowfile payload
                         * we include additional flowfile attributes including the response headers and the status codes.
                         */

                        // clone the flowfile to capture the response
                        if (requestFlowFile != null) {
                            responseFlowFile = session.create(requestFlowFile);
                        } else {
                            responseFlowFile = session.create();
                        }

                        // write attributes to response flowfile
                        responseFlowFile = session.putAllAttributes(responseFlowFile, statusAttributes);

                        // write the response headers as attributes
                        // this will overwrite any existing flowfile attributes
                        responseFlowFile = session.putAllAttributes(responseFlowFile, convertAttributesFromHeaders(url, responseHttp));

                        // transfer the message body to the payload
                        // can potentially be null in edge cases
                        if (bodyExists) {
                            // write content type attribute to response flowfile if it is available
                            if (responseBody.contentType() != null) {
                                 responseFlowFile = session.putAttribute(responseFlowFile, CoreAttributes.MIME_TYPE.key(), responseBody.contentType().toString());
                            }
                            if (teeInputStream != null) {
                                responseFlowFile = session.importFrom(teeInputStream, responseFlowFile);
                            } else {
                                responseFlowFile = session.importFrom(responseBodyStream, responseFlowFile);
                            }

                            // emit provenance event
                            final long millis = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNanos);
                            if(requestFlowFile != null) {
                                session.getProvenanceReporter().fetch(responseFlowFile, url.toExternalForm(), millis);
                            } else {
                                session.getProvenanceReporter().receive(responseFlowFile, url.toExternalForm(), millis);
                            }
                        }
                    }

                    // if not successful and request flowfile is not null, store the response body into a flowfile attribute
                    if (outputBodyToRequestAttribute && bodyExists) {
                        String attributeKey = context.getProperty(PROP_PUT_OUTPUT_IN_ATTRIBUTE).evaluateAttributeExpressions(requestFlowFile).getValue();
                        if (attributeKey == null) {
                            attributeKey = RESPONSE_BODY;
                        }
                        byte[] outputBuffer;
                        int size;

                        if (outputStreamToRequestAttribute != null) {
                            outputBuffer = outputStreamToRequestAttribute.getBuffer();
                            size = outputStreamToRequestAttribute.size();
                        } else {
                            outputBuffer = new byte[maxAttributeSize];
                            size = StreamUtils.fillBuffer(responseBodyStream, outputBuffer, false);
                        }
                        String bodyString = new String(outputBuffer, 0, size, getCharsetFromMediaType(responseBody.contentType()));
                        requestFlowFile = session.putAttribute(requestFlowFile, attributeKey, bodyString);

                        final long millis = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNanos);
                        session.getProvenanceReporter().modifyAttributes(requestFlowFile, "The " + attributeKey + " has been added. The value of which is the body of a http call to "
                                + url.toExternalForm() + ". It took " + millis + "millis,");
                    }
                } finally {
                    if(outputStreamToRequestAttribute != null){
                        outputStreamToRequestAttribute.close();
                        outputStreamToRequestAttribute = null;
                    }
                    if(teeInputStream != null){
                        teeInputStream.close();
                        teeInputStream = null;
                    } else if(responseBodyStream != null){
                        responseBodyStream.close();
                        responseBodyStream = null;
                    }
                }

                route(requestFlowFile, responseFlowFile, session, context, statusCode);

            }
        } catch (final Exception e) {
            // penalize or yield
            if (requestFlowFile != null) {
                logger.error("Routing to {} due to exception: {}", new Object[]{REL_FAILURE.getName(), e}, e);
                requestFlowFile = session.penalize(requestFlowFile);
                requestFlowFile = session.putAttribute(requestFlowFile, EXCEPTION_CLASS, e.getClass().getName());
                requestFlowFile = session.putAttribute(requestFlowFile, EXCEPTION_MESSAGE, e.getMessage());
                // transfer original to failure
                session.transfer(requestFlowFile, REL_FAILURE);
            } else {
                logger.error("Yielding processor due to exception encountered as a source processor: {}", e);
                context.yield();
            }

            // cleanup response flowfile, if applicable
            try {
                if (responseFlowFile != null) {
                    session.remove(responseFlowFile);
                }
            } catch (final Exception e1) {
                logger.error("Could not cleanup response flowfile due to exception: {}", new Object[]{e1}, e1);
            }
        }
    }

    @Override
    public void onTrigger(ProcessContext context, ProcessSession session) throws ProcessException {
        OkHttpClient okHttpClient = okHttpClientAtomicReference.get();

        FlowFile requestFlowFile = session.get();

        // Checking to see if the property to put the body of the response in an attribute was set
        boolean putToAttribute = context.getProperty(PROP_PUT_OUTPUT_IN_ATTRIBUTE).isSet();
        if (requestFlowFile == null) {
            if(context.hasNonLoopConnection()){
                return;
            }

            String request = context.getProperty(PROP_METHOD).evaluateAttributeExpressions().getValue().toUpperCase();
            if ("POST".equals(request) || "PUT".equals(request) || "PATCH".equals(request)) {
                return;
            } else if (putToAttribute) {
                requestFlowFile = session.create();
            }
        }

        // Setting some initial variables
        final int maxAttributeSize = context.getProperty(PROP_PUT_ATTRIBUTE_MAX_LENGTH).asInteger();
        final ComponentLog logger = getLogger();

        // log ETag cache metrics
        final boolean eTagEnabled = context.getProperty(PROP_USE_ETAG).asBoolean();
        if(eTagEnabled && logger.isDebugEnabled()) {
            final Cache cache = okHttpClient.cache();
            logger.debug("OkHttp ETag cache metrics :: Request Count: {} | Network Count: {} | Hit Count: {}",
                    new Object[] {cache.requestCount(), cache.networkCount(), cache.hitCount()});
        }

        // Every request/response cycle has a unique transaction id which will be stored as a flowfile attribute.
        final UUID txId = UUID.randomUUID();

        FlowFile responseFlowFile = null;
        try {
            // read the url property from the context
            final String urlstr = trimToEmpty(context.getProperty(PROP_URL).evaluateAttributeExpressions(requestFlowFile).getValue());
            final URL url = new URL(urlstr);

            Request httpRequest = configureRequest(context, session, requestFlowFile, url);

            // log request
            logRequest(logger, httpRequest);

            // emit send provenance event if successfully sent to the server
            if (httpRequest.body() != null) {
                session.getProvenanceReporter().send(requestFlowFile, url.toExternalForm(), true);
            }

            final long startNanos = System.nanoTime();
            Response responseHttp = okHttpClient.newCall(httpRequest).execute();

            // output the raw response headers (DEBUG level only)
            logResponse(logger, url, responseHttp);

            // store the status code and message
            int statusCode = responseHttp.code();
            String statusMessage = responseHttp.message();

            if (statusCode == 0) {
                throw new IllegalStateException("Status code unknown, connection hasn't been attempted.");
            }

            // Create a map of the status attributes that are always written to the request and response FlowFiles
            Map<String, String> statusAttributes = new HashMap<>();
            statusAttributes.put(STATUS_CODE, String.valueOf(statusCode));
            statusAttributes.put(STATUS_MESSAGE, statusMessage);
            statusAttributes.put(REQUEST_URL, url.toExternalForm());
            statusAttributes.put(TRANSACTION_ID, txId.toString());

            if (requestFlowFile != null) {
                requestFlowFile = session.putAllAttributes(requestFlowFile, statusAttributes);
            }

            // If the property to add the response headers to the request flowfile is true then add them
            if (context.getProperty(PROP_ADD_HEADERS_TO_REQUEST).asBoolean() && requestFlowFile != null) {
                // write the response headers as attributes
                // this will overwrite any existing flowfile attributes
                requestFlowFile = session.putAllAttributes(requestFlowFile, convertAttributesFromHeaders(url, responseHttp));
            }

            boolean outputBodyToRequestAttribute = (!isSuccess(statusCode) || putToAttribute) && requestFlowFile != null;
            boolean outputBodyToResponseContent = (isSuccess(statusCode) && !putToAttribute) || context.getProperty(PROP_OUTPUT_RESPONSE_REGARDLESS).asBoolean();
            ResponseBody responseBody = responseHttp.body();
            boolean bodyExists = responseBody != null;

            InputStream responseBodyStream = null;
            SoftLimitBoundedByteArrayOutputStream outputStreamToRequestAttribute = null;
            TeeInputStream teeInputStream = null;
            try {
                responseBodyStream = bodyExists ? responseBody.byteStream() : null;
                if (responseBodyStream != null && outputBodyToRequestAttribute && outputBodyToResponseContent) {
                    outputStreamToRequestAttribute = new SoftLimitBoundedByteArrayOutputStream(maxAttributeSize);
                    teeInputStream = new TeeInputStream(responseBodyStream, outputStreamToRequestAttribute);
                }

                if (outputBodyToResponseContent) {
                    /*
                     * If successful and putting to response flowfile, store the response body as the flowfile payload
                     * we include additional flowfile attributes including the response headers and the status codes.
                     */

                    // clone the flowfile to capture the response
                    if (requestFlowFile != null) {
                        responseFlowFile = session.create(requestFlowFile);
                    } else {
                        responseFlowFile = session.create();
                    }

                    // write attributes to response flowfile
                    responseFlowFile = session.putAllAttributes(responseFlowFile, statusAttributes);

                    // write the response headers as attributes
                    // this will overwrite any existing flowfile attributes
                    responseFlowFile = session.putAllAttributes(responseFlowFile, convertAttributesFromHeaders(url, responseHttp));

                    // transfer the message body to the payload
                    // can potentially be null in edge cases
                    if (bodyExists) {
                        // write content type attribute to response flowfile if it is available
                        if (responseBody.contentType() != null) {
                             responseFlowFile = session.putAttribute(responseFlowFile, CoreAttributes.MIME_TYPE.key(), responseBody.contentType().toString());
                        }
                        if (teeInputStream != null) {
                            responseFlowFile = session.importFrom(teeInputStream, responseFlowFile);
                        } else {
                            responseFlowFile = session.importFrom(responseBodyStream, responseFlowFile);
                        }

                        // emit provenance event
                        final long millis = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNanos);
                        if(requestFlowFile != null) {
                            session.getProvenanceReporter().fetch(responseFlowFile, url.toExternalForm(), millis);
                        } else {
                            session.getProvenanceReporter().receive(responseFlowFile, url.toExternalForm(), millis);
                        }
                    }
                }

                // if not successful and request flowfile is not null, store the response body into a flowfile attribute
                if (outputBodyToRequestAttribute && bodyExists) {
                    String attributeKey = context.getProperty(PROP_PUT_OUTPUT_IN_ATTRIBUTE).evaluateAttributeExpressions(requestFlowFile).getValue();
                    if (attributeKey == null) {
                        attributeKey = RESPONSE_BODY;
                    }
                    byte[] outputBuffer;
                    int size;

                    if (outputStreamToRequestAttribute != null) {
                        outputBuffer = outputStreamToRequestAttribute.getBuffer();
                        size = outputStreamToRequestAttribute.size();
                    } else {
                        outputBuffer = new byte[maxAttributeSize];
                        size = StreamUtils.fillBuffer(responseBodyStream, outputBuffer, false);
                    }
                    String bodyString = new String(outputBuffer, 0, size, getCharsetFromMediaType(responseBody.contentType()));
                    requestFlowFile = session.putAttribute(requestFlowFile, attributeKey, bodyString);

                    final long millis = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNanos);
                    session.getProvenanceReporter().modifyAttributes(requestFlowFile, "The " + attributeKey + " has been added. The value of which is the body of a http call to "
                            + url.toExternalForm() + ". It took " + millis + "millis,");
                }
            } finally {
                if(outputStreamToRequestAttribute != null){
                    outputStreamToRequestAttribute.close();
                    outputStreamToRequestAttribute = null;
                }
                if(teeInputStream != null){
                    teeInputStream.close();
                    teeInputStream = null;
                } else if(responseBodyStream != null){
                    responseBodyStream.close();
                    responseBodyStream = null;
                }
            }

            route(requestFlowFile, responseFlowFile, session, context, statusCode);
        } catch (final Exception e) {
            // penalize or yield
            if (requestFlowFile != null) {
                logger.error("Routing to {} due to exception: {}", new Object[]{REL_FAILURE.getName(), e}, e);
                requestFlowFile = session.penalize(requestFlowFile);
                requestFlowFile = session.putAttribute(requestFlowFile, EXCEPTION_CLASS, e.getClass().getName());
                requestFlowFile = session.putAttribute(requestFlowFile, EXCEPTION_MESSAGE, e.getMessage());
                // transfer original to failure
                session.transfer(requestFlowFile, REL_FAILURE);
            } else {
                logger.error("Yielding processor due to exception encountered as a source processor: {}", e);
                context.yield();
            }

            // cleanup response flowfile, if applicable
            try {
                if (responseFlowFile != null) {
                    session.remove(responseFlowFile);
                }
            } catch (final Exception e1) {
                logger.error("Could not cleanup response flowfile due to exception: {}", new Object[]{e1}, e1);
            }
        }
    }

Try With Resources try(Response responseHttp=okHttpClient.newCall(httpRequest).execute()) in method public onTrigger(context ProcessContext, session ProcessSession) : void from class org.apache.nifi.processors.standard.InvokeHTTP


    private BiPredicate<Path, BasicFileAttributes> createFileFilter(final ProcessContext context) {
        final long minSize = context.getProperty(MIN_SIZE).asDataSize(DataUnit.B).longValue();
        final Double maxSize = context.getProperty(MAX_SIZE).asDataSize(DataUnit.B);
        final long minAge = context.getProperty(MIN_AGE).asTimePeriod(TimeUnit.MILLISECONDS);
        final Long maxAge = context.getProperty(MAX_AGE).asTimePeriod(TimeUnit.MILLISECONDS);
        final boolean ignoreHidden = context.getProperty(IGNORE_HIDDEN_FILES).asBoolean();
        final Pattern filePattern = Pattern.compile(context.getProperty(FILE_FILTER).getValue());
        final String indir = context.getProperty(DIRECTORY).evaluateAttributeExpressions().getValue();
        final boolean recurseDirs = context.getProperty(RECURSE).asBoolean();
        final String pathPatternStr = context.getProperty(PATH_FILTER).getValue();
        final Pattern pathPattern = (!recurseDirs || pathPatternStr == null) ? null : Pattern.compile(pathPatternStr);
        return (path, attributes) -> {
            if (minSize > attributes.size()) {
                return false;
            }
            if (maxSize != null && maxSize < attributes.size()) {
                return false;
            }
            final long fileAge = System.currentTimeMillis() - attributes.lastModifiedTime().toMillis();
            if (minAge > fileAge) {
                return false;
            }
            if (maxAge != null && maxAge < fileAge) {
                return false;
            }
            if (ignoreHidden && path.toFile().isHidden()) {
                return false;
            }
            if (pathPattern != null) {
                Path reldir = Paths.get(indir).relativize(path).getParent();
                if (reldir != null && !reldir.toString().isEmpty()) {
                    if (!pathPattern.matcher(reldir.toString()).matches()) {
                        return false;
                    }
                }
            }
            // Verify that we have at least read permissions on the file we're considering grabbing
            if (!Files.isReadable(path)) {
                return false;
            }
            return filePattern.matcher(path.getFileName().toString()).matches();
        };
    }

    private FileFilter createFileFilter(final ProcessContext context) {
        final long minSize = context.getProperty(MIN_SIZE).asDataSize(DataUnit.B).longValue();
        final Double maxSize = context.getProperty(MAX_SIZE).asDataSize(DataUnit.B);
        final long minAge = context.getProperty(MIN_AGE).asTimePeriod(TimeUnit.MILLISECONDS);
        final Long maxAge = context.getProperty(MAX_AGE).asTimePeriod(TimeUnit.MILLISECONDS);
        final boolean ignoreHidden = context.getProperty(IGNORE_HIDDEN_FILES).asBoolean();
        final Pattern filePattern = Pattern.compile(context.getProperty(FILE_FILTER).getValue());
        final String indir = context.getProperty(DIRECTORY).evaluateAttributeExpressions().getValue();
        final boolean recurseDirs = context.getProperty(RECURSE).asBoolean();
        final String pathPatternStr = context.getProperty(PATH_FILTER).getValue();
        final Pattern pathPattern = (!recurseDirs || pathPatternStr == null) ? null : Pattern.compile(pathPatternStr);

        return new FileFilter() {
            @Override
            public boolean accept(final File file) {
                if (minSize > file.length()) {
                    return false;
                }
                if (maxSize != null && maxSize < file.length()) {
                    return false;
                }
                final long fileAge = System.currentTimeMillis() - file.lastModified();
                if (minAge > fileAge) {
                    return false;
                }
                if (maxAge != null && maxAge < fileAge) {
                    return false;
                }
                if (ignoreHidden && file.isHidden()) {
                    return false;
                }
                if (pathPattern != null) {
                    Path reldir = Paths.get(indir).relativize(file.toPath()).getParent();
                    if (reldir != null && !reldir.toString().isEmpty()) {
                        if (!pathPattern.matcher(reldir.toString()).matches()) {
                            return false;
                        }
                    }
                }
                //Verify that we have at least read permissions on the file we're considering grabbing
                if (!Files.isReadable(file.toPath())) {
                    return false;
                }
                return filePattern.matcher(file.getName()).matches();
            }
        };
    }

Replace Anonymous With Lambda org.apache.nifi.processors.standard.ListFile.createFileFilter.new FileFilter with (path, attributes) -> {if(minSize > attributes.size()){return false; in method private createFileFilter(context ProcessContext) : BiPredicate<Path,BasicFileAttributes> from class org.apache.nifi.processors.standard.ListFile


    @Override
    public Set<String> getContainerNames() {
        return new HashSet<>();
    }

    @Override
    public Set<String> getContainerNames() {
        return new HashSet<String>();
    }

Replace Generic With Diamond HashSet<String> with HashSet<> in method public getContainerNames() : Set<String> from class org.apache.nifi.provenance.MockProvenanceRepository


    public MinimalLockingWriteAheadLog(final Path path, final int partitionCount, final SerDe<T> serde, final SyncListener syncListener) throws IOException {
        this(new TreeSet<>(Collections.singleton(path)), partitionCount, new SingletonSerDeFactory<>(serde), syncListener);
    }

    public MinimalLockingWriteAheadLog(final Path path, final int partitionCount, final SerDe<T> serde, final SyncListener syncListener) throws IOException {
        this(new TreeSet<>(Collections.singleton(path)), partitionCount, new SingletonSerDeFactory<T>(serde), syncListener);
    }

Replace Generic With Diamond SingletonSerDeFactory<T> with SingletonSerDeFactory<> in method public MinimalLockingWriteAheadLog(path Path, partitionCount int, serde SerDe<T>, syncListener SyncListener) from class org.wali.MinimalLockingWriteAheadLog


    public MinimalLockingWriteAheadLog(final SortedSet<Path> paths, final int partitionCount, final SerDe<T> serde, final SyncListener syncListener) throws IOException {
        this(paths, partitionCount, new SingletonSerDeFactory<>(serde), syncListener);
    }

    public MinimalLockingWriteAheadLog(final SortedSet<Path> paths, final int partitionCount, final SerDe<T> serde, final SyncListener syncListener) throws IOException {
        this(paths, partitionCount, new SingletonSerDeFactory<T>(serde), syncListener);
    }

Replace Generic With Diamond SingletonSerDeFactory<T> with SingletonSerDeFactory<> in method public MinimalLockingWriteAheadLog(paths SortedSet<Path>, partitionCount int, serde SerDe<T>, syncListener SyncListener) from class org.wali.MinimalLockingWriteAheadLog


    /**
     * Test deletion of processor when connected to another
     *
     * @throws Exception exception
     */
    @Test
    public void validateProcessorDeletion() throws Exception {
        final FlowControllerAndSystemBundle fcsb = this.buildFlowControllerForTest();
        fc = fcsb.getFlowController();

        ProcessGroup testGroup = fc.createProcessGroup(UUID.randomUUID().toString());
        this.setControllerRootGroup(fc, testGroup);

        ProcessorNode testProcNodeA = fc.createProcessor(TestProcessor.class.getName(), UUID.randomUUID().toString(),
                fcsb.getSystemBundle().getBundleDetails().getCoordinate());
        testProcNodeA.setProperties(properties);
        testGroup.addProcessor(testProcNodeA);

        ProcessorNode testProcNodeB = fc.createProcessor(TestProcessor.class.getName(), UUID.randomUUID().toString(),
                fcsb.getSystemBundle().getBundleDetails().getCoordinate());
        testProcNodeB.setProperties(properties);
        testGroup.addProcessor(testProcNodeB);

        Collection<String> relationNames = new ArrayList<>();
        relationNames.add("relation");
        Connection connection = fc.createConnection(UUID.randomUUID().toString(), Connection.class.getName(), testProcNodeA, testProcNodeB, relationNames);
        testGroup.addConnection(connection);

        ProcessScheduler ps = fc.getProcessScheduler();
        ps.startProcessor(testProcNodeA, true);
        ps.startProcessor(testProcNodeB, true);

        try {
            testGroup.removeProcessor(testProcNodeA);
            fail();
        } catch (Exception e) {
            // should throw exception because processor running
        }

        try {
            testGroup.removeProcessor(testProcNodeB);
            fail();
        } catch (Exception e) {
            // should throw exception because processor running
        }

        ps.stopProcessor(testProcNodeB);
        Thread.sleep(100);

        try {
            testGroup.removeProcessor(testProcNodeA);
            fail();
        } catch (Exception e) {
            // should throw exception because destination processor running
        }

        try {
            testGroup.removeProcessor(testProcNodeB);
            fail();
        } catch (Exception e) {
            // should throw exception because source processor running
        }

        ps.stopProcessor(testProcNodeA);
        Thread.sleep(100);

        testGroup.removeProcessor(testProcNodeA);
        testGroup.removeProcessor(testProcNodeB);
        testGroup.shutdown();
    }

    /**
     * Test deletion of processor when connected to another
     *
     * @throws Exception exception
     */
    @Test
    public void validateProcessorDeletion() throws Exception {
        final FlowControllerAndSystemBundle fcsb = this.buildFlowControllerForTest();
        fc = fcsb.getFlowController();

        ProcessGroup testGroup = fc.createProcessGroup(UUID.randomUUID().toString());
        this.setControllerRootGroup(fc, testGroup);

        ProcessorNode testProcNodeA = fc.createProcessor(TestProcessor.class.getName(), UUID.randomUUID().toString(),
                fcsb.getSystemBundle().getBundleDetails().getCoordinate());
        testProcNodeA.setProperties(properties);
        testGroup.addProcessor(testProcNodeA);

        ProcessorNode testProcNodeB = fc.createProcessor(TestProcessor.class.getName(), UUID.randomUUID().toString(),
                fcsb.getSystemBundle().getBundleDetails().getCoordinate());
        testProcNodeB.setProperties(properties);
        testGroup.addProcessor(testProcNodeB);

        Collection<String> relationNames = new ArrayList<String>();
        relationNames.add("relation");
        Connection connection = fc.createConnection(UUID.randomUUID().toString(), Connection.class.getName(), testProcNodeA, testProcNodeB, relationNames);
        testGroup.addConnection(connection);

        ProcessScheduler ps = fc.getProcessScheduler();
        ps.startProcessor(testProcNodeA, true);
        ps.startProcessor(testProcNodeB, true);

        try {
            testGroup.removeProcessor(testProcNodeA);
            fail();
        } catch (Exception e) {
            // should throw exception because processor running
        }

        try {
            testGroup.removeProcessor(testProcNodeB);
            fail();
        } catch (Exception e) {
            // should throw exception because processor running
        }

        ps.stopProcessor(testProcNodeB);
        Thread.sleep(100);

        try {
            testGroup.removeProcessor(testProcNodeA);
            fail();
        } catch (Exception e) {
            // should throw exception because destination processor running
        }

        try {
            testGroup.removeProcessor(testProcNodeB);
            fail();
        } catch (Exception e) {
            // should throw exception because source processor running
        }

        ps.stopProcessor(testProcNodeA);
        Thread.sleep(100);

        testGroup.removeProcessor(testProcNodeA);
        testGroup.removeProcessor(testProcNodeB);
        testGroup.shutdown();
    }

Replace Generic With Diamond ArrayList<String> with ArrayList<> in method public validateProcessorDeletion() : void from class org.apache.nifi.controller.scheduling.TestProcessorLifecycle


    /**
     * Extracts contents of the {@link FlowFile} as byte array.
     */
    private byte[] extractMessageBody(FlowFile flowFile, ProcessSession session) {
        final byte[] messageContent = new byte[(int) flowFile.getSize()];
        session.read(flowFile, in -> StreamUtils.fillBuffer(in, messageContent, true));
        return messageContent;
    }

    /**
     * Extracts contents of the {@link FlowFile} as byte array.
     */
    private byte[] extractMessageBody(FlowFile flowFile, ProcessSession session) {
        final byte[] messageContent = new byte[(int) flowFile.getSize()];
        session.read(flowFile, new InputStreamCallback() {
            @Override
            public void process(final InputStream in) throws IOException {
                StreamUtils.fillBuffer(in, messageContent, true);
            }
        });
        return messageContent;
    }

Replace Anonymous With Lambda org.apache.nifi.jms.processors.PublishJMS.extractMessageBody.read.new InputStreamCallback with in -> StreamUtils.fillBuffer(in,messageContent,true) in method private extractMessageBody(flowFile FlowFile, session ProcessSession) : byte[] from class org.apache.nifi.jms.processors.PublishJMS


    @Override
    public void onTrigger(final ProcessContext context, final ProcessSession session) throws ProcessException {
        final ComponentLog logger = getLogger();

        final Document query = context.getProperty(QUERY).isSet()
                ? Document.parse(context.getProperty(QUERY).evaluateAttributeExpressions().getValue()) : null;
        final Document projection = context.getProperty(PROJECTION).isSet()
                ? Document.parse(context.getProperty(PROJECTION).evaluateAttributeExpressions().getValue()) : null;
        final Document sort = context.getProperty(SORT).isSet()
                ? Document.parse(context.getProperty(SORT).evaluateAttributeExpressions().getValue()) : null;
        final String jsonTypeSetting = context.getProperty(JSON_TYPE).getValue();
        final String usePrettyPrint  = context.getProperty(USE_PRETTY_PRINTING).getValue();
        configureMapper(jsonTypeSetting);

        final MongoCollection<Document> collection = getCollection(context);

        try {
            final FindIterable<Document> it = query != null ? collection.find(query) : collection.find();
            if (projection != null) {
                it.projection(projection);
            }
            if (sort != null) {
                it.sort(sort);
            }
            if (context.getProperty(LIMIT).isSet()) {
                it.limit(context.getProperty(LIMIT).evaluateAttributeExpressions().asInteger());
            }
            if (context.getProperty(BATCH_SIZE).isSet()) {
                it.batchSize(context.getProperty(BATCH_SIZE).evaluateAttributeExpressions().asInteger());
            }

            final MongoCursor<Document> cursor = it.iterator();
            ComponentLog log = getLogger();
            try {
                FlowFile flowFile = null;
                if (context.getProperty(RESULTS_PER_FLOWFILE).isSet()) {
                    int ceiling = context.getProperty(RESULTS_PER_FLOWFILE).evaluateAttributeExpressions().asInteger();
                    List<Document> batch = new ArrayList<>();

                    while (cursor.hasNext()) {
                        batch.add(cursor.next());
                        if (batch.size() == ceiling) {
                            try {
                                if (log.isDebugEnabled()) {
                                    log.debug("Writing batch...");
                                }
                                String payload = buildBatch(batch, jsonTypeSetting, usePrettyPrint);
                                writeBatch(payload, context, session);
                                batch = new ArrayList<>();
                            } catch (IOException ex) {
                                getLogger().error("Error building batch", ex);
                            }
                        }
                    }
                    if (batch.size() > 0) {
                        try {
                            writeBatch(buildBatch(batch, jsonTypeSetting, usePrettyPrint), context, session);
                        } catch (IOException ex) {
                            getLogger().error("Error sending remainder of batch", ex);
                        }
                    }
                } else {
                    while (cursor.hasNext()) {
                        flowFile = session.create();
                        flowFile = session.write(flowFile, out -> {
                            String json;
                            if (jsonTypeSetting.equals(JSON_TYPE_STANDARD)) {
                                json = getObjectWriter(mapper, usePrettyPrint).writeValueAsString(cursor.next());
                            } else {
                                json = cursor.next().toJson();
                            }
                            IOUtils.write(json, out);
                        });
                        flowFile = session.putAttribute(flowFile, CoreAttributes.MIME_TYPE.key(), "application/json");

                        session.getProvenanceReporter().receive(flowFile, getURI(context));
                        session.transfer(flowFile, REL_SUCCESS);
                    }
                }

                session.commit();

            } finally {
                cursor.close();
            }

        } catch (final RuntimeException e) {
            context.yield();
            session.rollback();
            logger.error("Failed to execute query {} due to {}", new Object[] { query, e }, e);
        }
    }

    @Override
    public void onTrigger(final ProcessContext context, final ProcessSession session) throws ProcessException {
        final ComponentLog logger = getLogger();

        final Document query = context.getProperty(QUERY).isSet()
                ? Document.parse(context.getProperty(QUERY).evaluateAttributeExpressions().getValue()) : null;
        final Document projection = context.getProperty(PROJECTION).isSet()
                ? Document.parse(context.getProperty(PROJECTION).evaluateAttributeExpressions().getValue()) : null;
        final Document sort = context.getProperty(SORT).isSet()
                ? Document.parse(context.getProperty(SORT).evaluateAttributeExpressions().getValue()) : null;
        final String jsonTypeSetting = context.getProperty(JSON_TYPE).getValue();
        configureMapper(jsonTypeSetting);

        final MongoCollection<Document> collection = getCollection(context);

        try {
            final FindIterable<Document> it = query != null ? collection.find(query) : collection.find();
            if (projection != null) {
                it.projection(projection);
            }
            if (sort != null) {
                it.sort(sort);
            }
            if (context.getProperty(LIMIT).isSet()) {
                it.limit(context.getProperty(LIMIT).evaluateAttributeExpressions().asInteger());
            }
            if (context.getProperty(BATCH_SIZE).isSet()) {
                it.batchSize(context.getProperty(BATCH_SIZE).evaluateAttributeExpressions().asInteger());
            }

            final MongoCursor<Document> cursor = it.iterator();
            ComponentLog log = getLogger();
            try {
                FlowFile flowFile = null;
                if (context.getProperty(RESULTS_PER_FLOWFILE).isSet()) {
                    int ceiling = context.getProperty(RESULTS_PER_FLOWFILE).evaluateAttributeExpressions().asInteger();
                    List<Document> batch = new ArrayList<>();

                    while (cursor.hasNext()) {
                        batch.add(cursor.next());
                        if (batch.size() == ceiling) {
                            try {
                                if (log.isDebugEnabled()) {
                                    log.debug("Writing batch...");
                                }
                                String payload = buildBatch(batch, jsonTypeSetting);
                                writeBatch(payload, context, session);
                                batch = new ArrayList<>();
                            } catch (IOException ex) {
                                getLogger().error("Error building batch", ex);
                            }
                        }
                    }
                    if (batch.size() > 0) {
                        try {
                            writeBatch(buildBatch(batch, jsonTypeSetting), context, session);
                        } catch (IOException ex) {
                            getLogger().error("Error sending remainder of batch", ex);
                        }
                    }
                } else {
                    while (cursor.hasNext()) {
                        flowFile = session.create();
                        flowFile = session.write(flowFile, new OutputStreamCallback() {
                            @Override
                            public void process(OutputStream out) throws IOException {
                                String json;
                                if (jsonTypeSetting.equals(JSON_TYPE_STANDARD)) {
                                    json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(cursor.next());
                                } else {
                                    json = cursor.next().toJson();
                                }
                                IOUtils.write(json, out);
                            }
                        });
                        flowFile = session.putAttribute(flowFile, CoreAttributes.MIME_TYPE.key(), "application/json");

                        session.getProvenanceReporter().receive(flowFile, getURI(context));
                        session.transfer(flowFile, REL_SUCCESS);
                    }
                }

                session.commit();

            } finally {
                cursor.close();
            }

        } catch (final RuntimeException e) {
            context.yield();
            session.rollback();
            logger.error("Failed to execute query {} due to {}", new Object[] { query, e }, e);
        }
    }

Replace Anonymous With Lambda org.apache.nifi.processors.mongodb.GetMongo.onTrigger.write.new OutputStreamCallback with out -> {String json; in method public onTrigger(context ProcessContext, session ProcessSession) : void from class org.apache.nifi.processors.mongodb.GetMongo


    /**
     * Creates a component using the optimistic locking manager.
     *
     * @param componentDto the DTO that will be used to create the component
     * @param daoCreation  A Supplier that will create the NiFi Component to use
     * @param dtoCreation  a Function that will convert the NiFi Component into a corresponding DTO
     * @param <D>          the DTO Type
     * @param <C>          the NiFi Component Type
     * @return a RevisionUpdate that represents the updated configuration
     */
    private <D, C> RevisionUpdate<D> createComponent(final Revision revision, final ComponentDTO componentDto, final Supplier<C> daoCreation, final Function<C, D> dtoCreation) {
        final NiFiUser user = NiFiUserUtils.getNiFiUser();

        // read lock on the containing group
        // request claim for component to be created... revision already verified (version == 0)
        final RevisionClaim claim = new StandardRevisionClaim(revision);

        // update revision through revision manager
        return revisionManager.updateRevision(claim, user, () -> {
            // add the component
            final C component = daoCreation.get();

            // save the flow
            controllerFacade.save();

            final D dto = dtoCreation.apply(component);
            final FlowModification lastMod = new FlowModification(revision.incrementRevision(revision.getClientId()), user.getIdentity());
            return new StandardRevisionUpdate<>(dto, lastMod);
        });
    }

    /**
     * Creates a component using the optimistic locking manager.
     *
     * @param componentDto the DTO that will be used to create the component
     * @param daoCreation  A Supplier that will create the NiFi Component to use
     * @param dtoCreation  a Function that will convert the NiFi Component into a corresponding DTO
     * @param <D>          the DTO Type
     * @param <C>          the NiFi Component Type
     * @return a RevisionUpdate that represents the updated configuration
     */
    private <D, C> RevisionUpdate<D> createComponent(final Revision revision, final ComponentDTO componentDto, final Supplier<C> daoCreation, final Function<C, D> dtoCreation) {
        final NiFiUser user = NiFiUserUtils.getNiFiUser();

        // read lock on the containing group
        // request claim for component to be created... revision already verified (version == 0)
        final RevisionClaim claim = new StandardRevisionClaim(revision);

        // update revision through revision manager
        return revisionManager.updateRevision(claim, user, () -> {
            // add the component
            final C component = daoCreation.get();

            // save the flow
            controllerFacade.save();

            final D dto = dtoCreation.apply(component);
            final FlowModification lastMod = new FlowModification(revision.incrementRevision(revision.getClientId()), user.getIdentity());
            return new StandardRevisionUpdate<D>(dto, lastMod);
        });
    }

Replace Generic With Diamond StandardRevisionUpdate<D> with StandardRevisionUpdate<> in method private createComponent(revision Revision, componentDto ComponentDTO, daoCreation Supplier<C>, dtoCreation Function<C,D>) : RevisionUpdate<D> from class org.apache.nifi.web.StandardNiFiServiceFacade


    @Override
    public SnippetEntity createSnippet(final SnippetDTO snippetDTO) {
        // add the component
        final Snippet snippet = snippetDAO.createSnippet(snippetDTO);

        // save the flow
        controllerFacade.save();

        final SnippetDTO dto = dtoFactory.createSnippetDto(snippet);
        final RevisionUpdate<SnippetDTO> snapshot = new StandardRevisionUpdate<>(dto, null);

        return entityFactory.createSnippetEntity(snapshot.getComponent());
    }

    @Override
    public SnippetEntity createSnippet(final SnippetDTO snippetDTO) {
        // add the component
        final Snippet snippet = snippetDAO.createSnippet(snippetDTO);

        // save the flow
        controllerFacade.save();

        final SnippetDTO dto = dtoFactory.createSnippetDto(snippet);
        final RevisionUpdate<SnippetDTO> snapshot = new StandardRevisionUpdate<SnippetDTO>(dto, null);

        return entityFactory.createSnippetEntity(snapshot.getComponent());
    }

Replace Generic With Diamond StandardRevisionUpdate<SnippetDTO> with StandardRevisionUpdate<> in method public createSnippet(snippetDTO SnippetDTO) : SnippetEntity from class org.apache.nifi.web.StandardNiFiServiceFacade


    @Override
    public RegistryClientEntity updateRegistryClient(Revision revision, RegistryDTO registryDTO) {
        final RevisionClaim revisionClaim = new StandardRevisionClaim(revision);
        final NiFiUser user = NiFiUserUtils.getNiFiUser();

        final FlowRegistry registry = registryDAO.getFlowRegistry(registryDTO.getId());
        final RevisionUpdate<FlowRegistry> revisionUpdate = revisionManager.updateRevision(revisionClaim, user, () -> {
            registry.setDescription(registryDTO.getDescription());
            registry.setName(registryDTO.getName());
            registry.setURL(registryDTO.getUri());

            controllerFacade.save();

            final Revision updatedRevision = revisionManager.getRevision(revision.getComponentId()).incrementRevision(revision.getClientId());
            final FlowModification lastModification = new FlowModification(updatedRevision, user.getIdentity());

            return new StandardRevisionUpdate<>(registry, lastModification);
        });

        final FlowRegistry updatedReg = revisionUpdate.getComponent();
        return createRegistryClientEntity(updatedReg);
    }

    @Override
    public RegistryClientEntity updateRegistryClient(Revision revision, RegistryDTO registryDTO) {
        final RevisionClaim revisionClaim = new StandardRevisionClaim(revision);
        final NiFiUser user = NiFiUserUtils.getNiFiUser();

        final FlowRegistry registry = registryDAO.getFlowRegistry(registryDTO.getId());
        final RevisionUpdate<FlowRegistry> revisionUpdate = revisionManager.updateRevision(revisionClaim, user, () -> {
            registry.setDescription(registryDTO.getDescription());
            registry.setName(registryDTO.getName());
            registry.setURL(registryDTO.getUri());

            controllerFacade.save();

            final Revision updatedRevision = revisionManager.getRevision(revision.getComponentId()).incrementRevision(revision.getClientId());
            final FlowModification lastModification = new FlowModification(updatedRevision, user.getIdentity());

            return new StandardRevisionUpdate<FlowRegistry>(registry, lastModification);
        });

        final FlowRegistry updatedReg = revisionUpdate.getComponent();
        return createRegistryClientEntity(updatedReg);
    }

Replace Generic With Diamond StandardRevisionUpdate<FlowRegistry> with StandardRevisionUpdate<> in method public updateRegistryClient(revision Revision, registryDTO RegistryDTO) : RegistryClientEntity from class org.apache.nifi.web.StandardNiFiServiceFacade


    @Override
    public ReportingTaskEntity createReportingTask(final Revision revision, final ReportingTaskDTO reportingTaskDTO) {
        final NiFiUser user = NiFiUserUtils.getNiFiUser();

        // request claim for component to be created... revision already verified (version == 0)
        final RevisionClaim claim = new StandardRevisionClaim(revision);

        // update revision through revision manager
        final RevisionUpdate<ReportingTaskDTO> snapshot = revisionManager.updateRevision(claim, user, () -> {
            // create the reporting task
            final ReportingTaskNode reportingTask = reportingTaskDAO.createReportingTask(reportingTaskDTO);

            // save the update
            controllerFacade.save();

            final ReportingTaskDTO dto = dtoFactory.createReportingTaskDto(reportingTask);
            final FlowModification lastMod = new FlowModification(revision.incrementRevision(revision.getClientId()), user.getIdentity());
            return new StandardRevisionUpdate<>(dto, lastMod);
        });

        final ReportingTaskNode reportingTask = reportingTaskDAO.getReportingTask(reportingTaskDTO.getId());
        final PermissionsDTO permissions = dtoFactory.createPermissionsDto(reportingTask);
        final List<BulletinDTO> bulletins = dtoFactory.createBulletinDtos(bulletinRepository.findBulletinsForSource(reportingTask.getIdentifier()));
        final List<BulletinEntity> bulletinEntities = bulletins.stream().map(bulletin -> entityFactory.createBulletinEntity(bulletin, permissions.getCanRead())).collect(Collectors.toList());
        return entityFactory.createReportingTaskEntity(snapshot.getComponent(), dtoFactory.createRevisionDTO(snapshot.getLastModification()), permissions, bulletinEntities);
    }

    @Override
    public ReportingTaskEntity createReportingTask(final Revision revision, final ReportingTaskDTO reportingTaskDTO) {
        final NiFiUser user = NiFiUserUtils.getNiFiUser();

        // request claim for component to be created... revision already verified (version == 0)
        final RevisionClaim claim = new StandardRevisionClaim(revision);

        // update revision through revision manager
        final RevisionUpdate<ReportingTaskDTO> snapshot = revisionManager.updateRevision(claim, user, () -> {
            // create the reporting task
            final ReportingTaskNode reportingTask = reportingTaskDAO.createReportingTask(reportingTaskDTO);

            // save the update
            controllerFacade.save();

            final ReportingTaskDTO dto = dtoFactory.createReportingTaskDto(reportingTask);
            final FlowModification lastMod = new FlowModification(revision.incrementRevision(revision.getClientId()), user.getIdentity());
            return new StandardRevisionUpdate<ReportingTaskDTO>(dto, lastMod);
        });

        final ReportingTaskNode reportingTask = reportingTaskDAO.getReportingTask(reportingTaskDTO.getId());
        final PermissionsDTO permissions = dtoFactory.createPermissionsDto(reportingTask);
        final List<BulletinDTO> bulletins = dtoFactory.createBulletinDtos(bulletinRepository.findBulletinsForSource(reportingTask.getIdentifier()));
        final List<BulletinEntity> bulletinEntities = bulletins.stream().map(bulletin -> entityFactory.createBulletinEntity(bulletin, permissions.getCanRead())).collect(Collectors.toList());
        return entityFactory.createReportingTaskEntity(snapshot.getComponent(), dtoFactory.createRevisionDTO(snapshot.getLastModification()), permissions, bulletinEntities);
    }

Replace Generic With Diamond StandardRevisionUpdate<ReportingTaskDTO> with StandardRevisionUpdate<> in method public createReportingTask(revision Revision, reportingTaskDTO ReportingTaskDTO) : ReportingTaskEntity from class org.apache.nifi.web.StandardNiFiServiceFacade


    private List<FlowFile> get(final Connection connection, final ConnectionPoller poller, final boolean lockQueue) {
        if (lockQueue) {
            connection.lock();
        }

        try {
            final Set<FlowFileRecord> expired = new HashSet<>();
            final List<FlowFileRecord> newlySelected = poller.poll(connection, expired);
            removeExpired(expired, connection);

            if (newlySelected.isEmpty() && expired.isEmpty()) {
                return new ArrayList<>();
            }

            for (final FlowFileRecord flowFile : newlySelected) {
                registerDequeuedRecord(flowFile, connection);
            }

            return new ArrayList<>(newlySelected);
        } finally {
            if (lockQueue) {
                connection.unlock();
            }
        }
    }

    private List<FlowFile> get(final Connection connection, final ConnectionPoller poller, final boolean lockQueue) {
        if (lockQueue) {
            connection.lock();
        }

        try {
            final Set<FlowFileRecord> expired = new HashSet<>();
            final List<FlowFileRecord> newlySelected = poller.poll(connection, expired);
            removeExpired(expired, connection);

            if (newlySelected.isEmpty() && expired.isEmpty()) {
                return new ArrayList<>();
            }

            for (final FlowFileRecord flowFile : newlySelected) {
                registerDequeuedRecord(flowFile, connection);
            }

            return new ArrayList<FlowFile>(newlySelected);
        } finally {
            if (lockQueue) {
                connection.unlock();
            }
        }
    }

Replace Generic With Diamond ArrayList<FlowFile> with ArrayList<> in method private get(connection Connection, poller ConnectionPoller, lockQueue boolean) : List<FlowFile> from class org.apache.nifi.controller.repository.StandardProcessSession


    private List<FlowFile> get(final ConnectionPoller poller, final boolean lockAllQueues) {
        final List<Connection> connections = context.getPollableConnections();
        if (lockAllQueues) {
            for (final Connection connection : connections) {
                connection.lock();
            }
        }

        final int startIndex = context.getNextIncomingConnectionIndex();

        try {
            for (int i = 0; i < connections.size(); i++) {
                final int connectionIndex = (startIndex + i) % connections.size();
                final Connection conn = connections.get(connectionIndex);

                final Set<FlowFileRecord> expired = new HashSet<>();
                final List<FlowFileRecord> newlySelected = poller.poll(conn, expired);
                removeExpired(expired, conn);

                if (newlySelected.isEmpty() && expired.isEmpty()) {
                    continue;
                }

                for (final FlowFileRecord flowFile : newlySelected) {
                    registerDequeuedRecord(flowFile, conn);
                }

                return new ArrayList<>(newlySelected);
            }

            return new ArrayList<>();
        } finally {
            if (lockAllQueues) {
                for (final Connection connection : connections) {
                    connection.unlock();
                }
            }
        }
    }

    private List<FlowFile> get(final ConnectionPoller poller, final boolean lockAllQueues) {
        final List<Connection> connections = context.getPollableConnections();
        if (lockAllQueues) {
            for (final Connection connection : connections) {
                connection.lock();
            }
        }

        final int startIndex = context.getNextIncomingConnectionIndex();

        try {
            for (int i = 0; i < connections.size(); i++) {
                final int connectionIndex = (startIndex + i) % connections.size();
                final Connection conn = connections.get(connectionIndex);

                final Set<FlowFileRecord> expired = new HashSet<>();
                final List<FlowFileRecord> newlySelected = poller.poll(conn, expired);
                removeExpired(expired, conn);

                if (newlySelected.isEmpty() && expired.isEmpty()) {
                    continue;
                }

                for (final FlowFileRecord flowFile : newlySelected) {
                    registerDequeuedRecord(flowFile, conn);
                }

                return new ArrayList<FlowFile>(newlySelected);
            }

            return new ArrayList<>();
        } finally {
            if (lockAllQueues) {
                for (final Connection connection : connections) {
                    connection.unlock();
                }
            }
        }
    }

Replace Generic With Diamond ArrayList<FlowFile> with ArrayList<> in method private get(poller ConnectionPoller, lockAllQueues boolean) : List<FlowFile> from class org.apache.nifi.controller.repository.StandardProcessSession


        @Override
        public Set<String> getContainerNames() {
            return new HashSet<>();
        }

        @Override
        public Set<String> getContainerNames() {
            return new HashSet<String>();
        }

Replace Generic With Diamond HashSet<String> with HashSet<> in method public getContainerNames() : Set<String> from class org.apache.nifi.controller.repository.TestStandardProcessSession.MockContentRepository


    @Test
    public void validateSuccessfulInteraction() throws Exception, EmailException {
        int port = NetworkUtils.availablePort();

        TestRunner runner = TestRunners.newTestRunner(ListenSMTP.class);
        runner.setProperty(ListenSMTP.SMTP_PORT, String.valueOf(port));
        runner.setProperty(ListenSMTP.SMTP_MAXIMUM_CONNECTIONS, "3");

        runner.assertValid();
        runner.run(5, false);
        final int numMessages = 5;
        CountDownLatch latch = new CountDownLatch(numMessages);

        this.executor.schedule(() -> {
            for (int i = 0; i < numMessages; i++) {
                try {
                    Email email = new SimpleEmail();
                    email.setHostName("localhost");
                    email.setSmtpPort(port);
                    email.setFrom("alice@nifi.apache.org");
                    email.setSubject("This is a test");
                    email.setMsg("MSG-" + i);
                    email.addTo("bob@nifi.apache.org");
                    email.send();
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new RuntimeException(e);
                } finally {
                    latch.countDown();
                }
            }
        }, 1500, TimeUnit.MILLISECONDS);

        boolean complete = latch.await(5000, TimeUnit.MILLISECONDS);
        runner.shutdown();
        assertTrue(complete);
        runner.assertAllFlowFilesTransferred(ListenSMTP.REL_SUCCESS, numMessages);
    }

    @Test
    public void validateSuccessfulInteraction() throws Exception, EmailException {
        int port = NetworkUtils.availablePort();

        TestRunner runner = TestRunners.newTestRunner(ListenSMTP.class);
        runner.setProperty(ListenSMTP.SMTP_PORT, String.valueOf(port));
        runner.setProperty(ListenSMTP.SMTP_MAXIMUM_CONNECTIONS, "3");
        runner.setProperty(ListenSMTP.SMTP_TIMEOUT, "10 seconds");

        runner.assertValid();
        runner.run(5, false);
        final int numMessages = 5;
        CountDownLatch latch = new CountDownLatch(numMessages);

        this.executor.schedule(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < numMessages; i++) {
                    try {
                        Email email = new SimpleEmail();
                        email.setHostName("localhost");
                        email.setSmtpPort(port);
                        email.setFrom("alice@nifi.apache.org");
                        email.setSubject("This is a test");
                        email.setMsg("MSG-" + i);
                        email.addTo("bob@nifi.apache.org");
                        email.send();
                    } catch (Exception e) {
                        e.printStackTrace();
                        throw new RuntimeException(e);
                    } finally {
                        latch.countDown();
                    }
                }
            }
        }, 1500, TimeUnit.MILLISECONDS);

        boolean complete = latch.await(5000, TimeUnit.MILLISECONDS);
        runner.shutdown();
        assertTrue(complete);
        runner.assertAllFlowFilesTransferred(ListenSMTP.REL_SUCCESS, numMessages);
    }

Replace Anonymous With Lambda org.apache.nifi.processors.email.TestListenSMTP.validateSuccessfulInteraction.schedule.new Runnable with () -> {for(int i=0; i < numMessages; i++){tryEmail email=new SimpleEmail(); in method public validateSuccessfulInteraction() : void from class org.apache.nifi.processors.email.TestListenSMTP


    @Test
    public void validateSuccessfulInteractionWithTls() throws Exception, EmailException {
        System.setProperty("mail.smtp.ssl.trust", "*");
        System.setProperty("javax.net.ssl.keyStore", "src/test/resources/localhost-ks.jks");
        System.setProperty("javax.net.ssl.keyStorePassword", "localtest");
        int port = NetworkUtils.availablePort();

        TestRunner runner = TestRunners.newTestRunner(ListenSMTP.class);
        runner.setProperty(ListenSMTP.SMTP_PORT, String.valueOf(port));
        runner.setProperty(ListenSMTP.SMTP_MAXIMUM_CONNECTIONS, "3");

        // Setup the SSL Context
        SSLContextService sslContextService = new StandardSSLContextService();
        runner.addControllerService("ssl-context", sslContextService);
        runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE, "src/test/resources/localhost-ts.jks");
        runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE_PASSWORD, "localtest");
        runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE_TYPE, "JKS");
        runner.setProperty(sslContextService, StandardSSLContextService.KEYSTORE, "src/test/resources/localhost-ks.jks");
        runner.setProperty(sslContextService, StandardSSLContextService.KEYSTORE_PASSWORD, "localtest");
        runner.setProperty(sslContextService, StandardSSLContextService.KEYSTORE_TYPE, "JKS");
        runner.enableControllerService(sslContextService);

        // and add the SSL context to the runner
        runner.setProperty(ListenSMTP.SSL_CONTEXT_SERVICE, "ssl-context");
        runner.setProperty(ListenSMTP.CLIENT_AUTH, SSLContextService.ClientAuth.NONE.name());
        runner.assertValid();

        int messageCount = 5;
        CountDownLatch latch = new CountDownLatch(messageCount);
        runner.run(messageCount, false);

        this.executor.schedule(() -> {
            for (int i = 0; i < messageCount; i++) {
                try {
                    Email email = new SimpleEmail();
                    email.setHostName("localhost");
                    email.setSmtpPort(port);
                    email.setFrom("alice@nifi.apache.org");
                    email.setSubject("This is a test");
                    email.setMsg("MSG-" + i);
                    email.addTo("bob@nifi.apache.org");

                    // Enable STARTTLS but ignore the cert
                    email.setStartTLSEnabled(true);
                    email.setStartTLSRequired(true);
                    email.setSSLCheckServerIdentity(false);
                    email.send();
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new RuntimeException(e);
                } finally {
                    latch.countDown();
                }
            }
        }, 1500, TimeUnit.MILLISECONDS);

        boolean complete = latch.await(5000, TimeUnit.MILLISECONDS);
        runner.shutdown();
        assertTrue(complete);
        runner.assertAllFlowFilesTransferred("success", messageCount);
    }

    @Test
    public void validateSuccessfulInteractionWithTls() throws Exception, EmailException {
        System.setProperty("mail.smtp.ssl.trust", "*");
        System.setProperty("javax.net.ssl.keyStore", "src/test/resources/localhost-ks.jks");
        System.setProperty("javax.net.ssl.keyStorePassword", "localtest");
        int port = NetworkUtils.availablePort();

        TestRunner runner = TestRunners.newTestRunner(ListenSMTP.class);
        runner.setProperty(ListenSMTP.SMTP_PORT, String.valueOf(port));
        runner.setProperty(ListenSMTP.SMTP_MAXIMUM_CONNECTIONS, "3");
        runner.setProperty(ListenSMTP.SMTP_TIMEOUT, "10 seconds");

        // Setup the SSL Context
        SSLContextService sslContextService = new StandardSSLContextService();
        runner.addControllerService("ssl-context", sslContextService);
        runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE, "src/test/resources/localhost-ts.jks");
        runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE_PASSWORD, "localtest");
        runner.setProperty(sslContextService, StandardSSLContextService.TRUSTSTORE_TYPE, "JKS");
        runner.setProperty(sslContextService, StandardSSLContextService.KEYSTORE, "src/test/resources/localhost-ks.jks");
        runner.setProperty(sslContextService, StandardSSLContextService.KEYSTORE_PASSWORD, "localtest");
        runner.setProperty(sslContextService, StandardSSLContextService.KEYSTORE_TYPE, "JKS");
        runner.enableControllerService(sslContextService);

        // and add the SSL context to the runner
        runner.setProperty(ListenSMTP.SSL_CONTEXT_SERVICE, "ssl-context");
        runner.setProperty(ListenSMTP.CLIENT_AUTH, SSLContextService.ClientAuth.NONE.name());
        runner.assertValid();

        int messageCount = 5;
        CountDownLatch latch = new CountDownLatch(messageCount);
        runner.run(messageCount, false);

        this.executor.schedule(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < messageCount; i++) {
                    try {
                        Email email = new SimpleEmail();
                        email.setHostName("localhost");
                        email.setSmtpPort(port);
                        email.setFrom("alice@nifi.apache.org");
                        email.setSubject("This is a test");
                        email.setMsg("MSG-" + i);
                        email.addTo("bob@nifi.apache.org");

                        // Enable STARTTLS but ignore the cert
                        email.setStartTLSEnabled(true);
                        email.setStartTLSRequired(true);
                        email.setSSLCheckServerIdentity(false);
                        email.send();
                    } catch (Exception e) {
                        e.printStackTrace();
                        throw new RuntimeException(e);
                    } finally {
                        latch.countDown();
                    }
                }
            }
        }, 1500, TimeUnit.MILLISECONDS);

        boolean complete = latch.await(5000, TimeUnit.MILLISECONDS);
        runner.shutdown();
        assertTrue(complete);
        runner.assertAllFlowFilesTransferred("success", messageCount);
    }

Replace Anonymous With Lambda org.apache.nifi.processors.email.TestListenSMTP.validateSuccessfulInteractionWithTls.schedule.new Runnable with () -> {for(int i=0; i < messageCount; i++){tryEmail email=new SimpleEmail(); in method public validateSuccessfulInteractionWithTls() : void from class org.apache.nifi.processors.email.TestListenSMTP


    @Test
    public void validateTooLargeMessage() throws Exception, EmailException {
        int port = NetworkUtils.availablePort();

        TestRunner runner = TestRunners.newTestRunner(ListenSMTP.class);
        runner.setProperty(ListenSMTP.SMTP_PORT, String.valueOf(port));
        runner.setProperty(ListenSMTP.SMTP_MAXIMUM_CONNECTIONS, "3");
        runner.setProperty(ListenSMTP.SMTP_MAXIMUM_MSG_SIZE, "10 B");

        runner.assertValid();

        int messageCount = 1;
        CountDownLatch latch = new CountDownLatch(messageCount);

        runner.run(messageCount, false);

        this.executor.schedule(() -> {
            for (int i = 0; i < messageCount; i++) {
                try {
                    Email email = new SimpleEmail();
                    email.setHostName("localhost");
                    email.setSmtpPort(port);
                    email.setFrom("alice@nifi.apache.org");
                    email.setSubject("This is a test");
                    email.setMsg("MSG-" + i);
                    email.addTo("bob@nifi.apache.org");
                    email.send();
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new RuntimeException(e);
                } finally {
                    latch.countDown();
                }
            }
        }, 1000, TimeUnit.MILLISECONDS);

        boolean complete = latch.await(5000, TimeUnit.MILLISECONDS);
        runner.shutdown();
        assertTrue(complete);
        runner.assertAllFlowFilesTransferred("success", 0);
    }

    @Test
    public void validateTooLargeMessage() throws Exception, EmailException {
        int port = NetworkUtils.availablePort();

        TestRunner runner = TestRunners.newTestRunner(ListenSMTP.class);
        runner.setProperty(ListenSMTP.SMTP_PORT, String.valueOf(port));
        runner.setProperty(ListenSMTP.SMTP_MAXIMUM_CONNECTIONS, "3");
        runner.setProperty(ListenSMTP.SMTP_TIMEOUT, "10 seconds");
        runner.setProperty(ListenSMTP.SMTP_MAXIMUM_MSG_SIZE, "10 B");

        runner.assertValid();

        int messageCount = 1;
        CountDownLatch latch = new CountDownLatch(messageCount);

        runner.run(messageCount, false);

        this.executor.schedule(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < messageCount; i++) {
                    try {
                        Email email = new SimpleEmail();
                        email.setHostName("localhost");
                        email.setSmtpPort(port);
                        email.setFrom("alice@nifi.apache.org");
                        email.setSubject("This is a test");
                        email.setMsg("MSG-" + i);
                        email.addTo("bob@nifi.apache.org");
                        email.send();
                    } catch (Exception e) {
                        e.printStackTrace();
                        throw new RuntimeException(e);
                    } finally {
                        latch.countDown();
                    }
                }
            }
        }, 1000, TimeUnit.MILLISECONDS);

        boolean complete = latch.await(5000, TimeUnit.MILLISECONDS);
        runner.shutdown();
        assertTrue(complete);
        runner.assertAllFlowFilesTransferred("success", 0);
    }

Replace Anonymous With Lambda org.apache.nifi.processors.email.TestListenSMTP.validateTooLargeMessage.schedule.new Runnable with () -> {for(int i=0; i < messageCount; i++){tryEmail email=new SimpleEmail(); in method public validateTooLargeMessage() : void from class org.apache.nifi.processors.email.TestListenSMTP


            @Override
            public ValidationResult validate(String subject, String input, ValidationContext context) {

                Grok grok = new Grok();
                try {
                    grok.compile(input);
                } catch (GrokException | java.util.regex.PatternSyntaxException e) {
                    return new ValidationResult.Builder()
                            .subject(subject)
                            .input(input)
                            .valid(false)
                            .explanation("Not a valid Grok Expression - " + e.getMessage())
                            .build();
                }

                return new ValidationResult.Builder().subject(subject).input(input).valid(true).build();
            }

            @Override
            public ValidationResult validate(String subject, String input, ValidationContext context) {

                Grok grok = new Grok();
                try {
                    grok.compile(input);
                } catch (GrokException e) {
                    return new ValidationResult.Builder()
                            .subject(subject)
                            .input(input)
                            .valid(false)
                            .explanation("Not a valid Grok Expression")
                            .build();
                } catch (java.util.regex.PatternSyntaxException e) {
                    return new ValidationResult.Builder()
                            .subject(subject)
                            .input(input)
                            .valid(false)
                            .explanation("Not a valid Grok Expression")
                            .build();
                }

                return new ValidationResult.Builder().subject(subject).input(input).valid(true).build();
            }

Merge Catch [catch(GrokException e), catch(PatternSyntaxException e)] to catch(GrokException|PatternSyntaxException e) in method public validate(subject String, input String, context ValidationContext) : ValidationResult from class org.apache.nifi.processors.standard.ExtractGrok.validateGrokExpression.new Validator