ws.argo.responder.ProbeHandlerThread.java Source code

Java tutorial

Introduction

Here is the source code for ws.argo.responder.ProbeHandlerThread.java

Source

/*
 * Copyright 2015 Jeff Simpson.
 *
 * Licensed under the MIT License, (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://opensource.org/licenses/MIT
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package ws.argo.responder;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import ws.argo.plugin.probehandler.ProbeHandlerPlugin;
import ws.argo.wireline.probe.ProbeWrapper;
import ws.argo.wireline.probe.ProbeWrapper.RespondToURL;
import ws.argo.wireline.response.ResponseWrapper;

/**
 * The ProbeHandlerThread is the worker thread for the {@link Responder}. The
 * Responder will launch a new ProbeHandlerThread with it receives a probe off
 * the wire. It will run through all of the probe handlers and process any
 * positive hits that it gets. It then compiles the results (discovered
 * Services), packages them up in a response and sends the response back to the
 * list of respondTo addresses in the probe. If it fails on one respondTo
 * address, the it will just move to the next (if there is one).
 * 
 * @author jmsimpson
 *
 */
public class ProbeHandlerThread implements Runnable {

    private static final Logger LOGGER = LogManager.getLogger(ProbeHandlerThread.class.getName());

    // 5 minutes
    private static final long probeCacheTimeout = 5 * 60 * 1000;

    private static Map<String, Long> handledProbes = new ConcurrentHashMap<String, Long>();

    protected CloseableHttpClient httpClient;

    ArrayList<ProbeHandlerPlugin> handlers;
    ProbeWrapper probe;
    boolean noBrowser;
    Responder responder;

    /**
     * Create a new ProbeHandler thread that will process a probe in a
     * multi-threaded way.
     * 
     * @param responder pointer to the instance of a responder associated
     * @param probe - the actual probe payload
     * @param noBrowser - a flag that indicated whether a naked probe should be
     *          processed
     */
    public ProbeHandlerThread(Responder responder, ProbeWrapper probe, boolean noBrowser) {
        this.responder = responder;
        this.handlers = responder.getHandlers();
        this.probe = probe;
        this.httpClient = responder.httpClient;
        this.noBrowser = noBrowser;
    }

    /**
     * If the probe yields responses from the handler, then send this method will
     * send the responses to the given respondTo addresses.
     * 
     * @param respondToURL - address to send the response to
     * @param payloadType - JSON or XML
     * @param payload - the actual service records to return
     * @return true if the send was successful
     */
    private boolean sendResponse(String respondToURL, String payloadType, ResponseWrapper payload) {

        // This method will likely need some thought and care in the error handling
        // and error reporting
        // It's a had job at the moment.

        String responseStr = null;
        String contentType = null; // MIME type
        boolean success = true;

        switch (payloadType) {
        case "XML": {
            responseStr = payload.toXML();
            contentType = "application/xml";
            break;
        }
        case "JSON": {
            responseStr = payload.toJSON();
            contentType = "application/json";
            break;
        }
        default:
            responseStr = payload.toJSON();
        }

        try {

            HttpPost postRequest = new HttpPost(respondToURL);

            StringEntity input = new StringEntity(responseStr);
            input.setContentType(contentType);
            postRequest.setEntity(input);

            LOGGER.debug("Sending response");
            LOGGER.debug("Response payload:");
            LOGGER.debug(responseStr);
            CloseableHttpResponse httpResponse = httpClient.execute(postRequest);
            try {

                int statusCode = httpResponse.getStatusLine().getStatusCode();
                if (statusCode > 300) {
                    throw new RuntimeException(
                            "Failed : HTTP error code : " + httpResponse.getStatusLine().getStatusCode());
                }

                if (statusCode != 204) {
                    BufferedReader br = new BufferedReader(
                            new InputStreamReader((httpResponse.getEntity().getContent())));

                    LOGGER.debug("Successful response from response target - " + respondToURL);
                    String output;
                    LOGGER.debug("Output from Listener .... \n");
                    while ((output = br.readLine()) != null) {
                        LOGGER.debug(output);
                    }
                    br.close();
                }
            } finally {
                httpResponse.close();
            }

            LOGGER.info("Successfully handled probeID: " + probe.getProbeId() + " sending response to: "
                    + respondToURL);

        } catch (MalformedURLException e) {
            success = false;
            LOGGER.error("MalformedURLException occured  for probeID [" + payload.getProbeID() + "]"
                    + "\nThe respondTo URL was a no good.  respondTo URL is: " + respondToURL);
        } catch (IOException e) {
            success = false;
            LOGGER.error("An IOException occured for probeID [" + payload.getProbeID() + "]" + " - "
                    + e.getLocalizedMessage());
            LOGGER.debug("Stack trace for IOException for probeID [" + payload.getProbeID() + "]", e);
        } catch (Exception e) {
            success = false;
            LOGGER.error("Some other error occured for probeID [" + payload.getProbeID() + "]"
                    + ".  respondTo URL is: " + respondToURL + " - " + e.getLocalizedMessage());
            LOGGER.debug("Stack trace for probeID [" + payload.getProbeID() + "]", e);
        }

        return success;

    }

    private void markProbeAsHandled(String probeID) {
        handledProbes.put(probeID, Long.valueOf(System.currentTimeMillis()));
    }

    private boolean isProbeHandled(String probeID) {

        boolean isProbeHandled = false;
        long now = System.currentTimeMillis();

        Long lastTime = handledProbes.get(probeID);

        if (lastTime != null) {
            long delta = now - lastTime.longValue();
            if (delta < probeCacheTimeout) {
                isProbeHandled = true; // yup, I have handled this before. If past
                                       // timeout, then it's like I never saw it before
            }
        }

        return isProbeHandled;
    }

    /**
     * Handle the probe.
     */
    public void run() {

        ResponseWrapper response = null;

        LOGGER.info("Received probe id: " + probe.getProbeId());

        // Only handle probes that we haven't handled before
        // The Probe Generator needs to send a stream of identical UDP packets
        // to compensate for UDP reliability issues. Therefore, the Responder
        // will likely get more than 1 identical probe. We should ignore
        // duplicates.
        if (!isProbeHandled(probe.getProbeId())) {

            if (this.noBrowser && probe.isNaked()) {
                LOGGER.warn("Responder set to noBrowser mode. Discarding naked probe with id [" + probe.getProbeId()
                        + "]");
            } else {

                for (ProbeHandlerPlugin handler : handlers) {
                    response = handler.handleProbeEvent(probe);
                    if (!response.isEmpty()) {
                        LOGGER.debug("Response to probe [" + probe.getProbeId() + "] includes "
                                + response.numberOfServices());
                        Iterator<RespondToURL> respondToURLs = probe.getRespondToURLs().iterator();

                        if (probe.getRespondToURLs().isEmpty())
                            LOGGER.warn("Processed probe [" + probe.getProbeId()
                                    + "] with no respondTo address. That's odd.");

                        if (respondToURLs.hasNext()) {
                            RespondToURL respondToURL = respondToURLs.next();
                            // we are ignoring the label for now
                            boolean success = sendResponse(respondToURL.url, probe.getRespondToPayloadType(),
                                    response);
                            if (!success) {
                                LOGGER.warn("Issue sending probe [" + probe.getProbeId() + "] response to ["
                                        + respondToURL.url + "]");
                            }
                        }

                    } else {
                        LOGGER.error("Response to probe [" + probe.getProbeId()
                                + "] is empty.  Not sending empty response.");
                    }
                }

            }

            markProbeAsHandled(probe.getProbeId());

            responder.probeProcessed();

        } else {
            LOGGER.info("Discarding duplicate/handled probe with id: " + probe.getProbeId());
        }
    }
}