All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.bytex.snamp.connector.http.AcceptorService Maven / Gradle / Ivy

package com.bytex.snamp.connector.http;

import com.bytex.snamp.MapUtils;
import com.bytex.snamp.connector.ManagedResourceConnector;
import com.bytex.snamp.connector.ManagedResourceConnectorClient;
import com.bytex.snamp.connector.dataStream.DataStreamConnector;
import com.bytex.snamp.core.ExposedServiceHandler;
import com.bytex.snamp.instrumentation.measurements.Measurement;
import com.bytex.snamp.scripting.groovy.xml.XmlSlurperSlim;
import com.sun.jersey.spi.resource.Singleton;
import groovy.json.JsonSlurper;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Version;
import org.xml.sax.SAXException;

import javax.ws.rs.*;
import javax.ws.rs.core.*;
import java.io.StringReader;
import java.util.*;

import static com.bytex.snamp.internal.Utils.getBundleContextOfObject;

/**
 * Represents REST service used to handle measurement and monitoring events through HTTP.
 * @author Roman Sakno
 * @version 2.0
 * @since 2.0
 */
@Singleton
@Path("/")
public final class AcceptorService {
    private static final class Payload{
        private final Map headers;
        private final Object body;

        private Payload(final Map headers, final Object body){
            this.headers = Objects.requireNonNull(headers);
            this.body = Objects.requireNonNull(body);
        }
    }

    private static final class CustomPayloadDispatcher extends ExposedServiceHandler {
        private CustomPayloadDispatcher() {
            super(ManagedResourceConnector.class);
        }

        @Override
        protected boolean handleService(final ManagedResourceConnector service, final Payload userData) throws Exception {
            if (service instanceof DataStreamConnector)
                ((DataStreamConnector) service).dispatch(userData.headers, userData.body);
            return true;
        }
    }

    private final JsonSlurper jsonParser;
    private final CustomPayloadDispatcher payloadDispatcher;

    AcceptorService() {
        jsonParser = new JsonSlurper();
        payloadDispatcher = new CustomPayloadDispatcher();
    }

    private static Map wrapHeaders(final HttpHeaders headers) {
        final MultivaluedMap requestHeaders = headers.getRequestHeaders();
        return MapUtils.readOnlyMap(requestHeaders::get, requestHeaders.keySet());
    }

    private static Response noContent(){
        return Response.noContent().build();
    }

    @GET
    @Path("/ping")
    @Produces(MediaType.TEXT_PLAIN)
    public Response ping() {
        final Version version = getBundleContextOfObject(this).getBundle().getVersion();
        final String httpAcceptorType = ManagedResourceConnector.getConnectorType(HttpAcceptor.class);
        final Set sources = new HashSet<>();
        final BundleContext context = getBundleContext();
        for (final String resourceName : ManagedResourceConnectorClient.selector().getResources(context))
            ManagedResourceConnectorClient.tryCreate(context, resourceName).ifPresent(client -> {
                final String connectorType = client.getConnectorType();
                if (Objects.equals(httpAcceptorType, connectorType))
                    sources.add(resourceName);
                client.close();
            });
        final String responseBody = String.format("HTTP Acceptor, version=%s, sources=%s", version, sources);
        return Response.ok()
                .entity(responseBody)
                .build();
    }

    @Path("/measurements")
    @POST
    @Produces(MediaType.TEXT_PLAIN)
    @Consumes(MediaType.APPLICATION_JSON)
    public Response acceptMeasurements(@Context final HttpHeaders headers, final Measurement[] measurements) {
        return Arrays.stream(measurements)
                .map(measurement -> acceptMeasurement(headers, measurement))
                .reduce(noContent(), (result, response) -> response.getStatus() > result.getStatus() ? response : result);
    }

    private BundleContext getBundleContext(){
        return getBundleContextOfObject(this);
    }

    private static Response acceptMeasurement(final ManagedResourceConnectorClient client,
                                              final HttpHeaders headers,
                                              final Measurement measurement) {
        try {
            return client.queryObject(DataStreamConnector.class)
                    .map(acceptor -> {
                        try {
                            acceptor.dispatch(wrapHeaders(headers), measurement);
                        } catch (final Exception e) {
                            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.toString()).build();
                        }
                        return noContent();
                    })
                    .orElseGet(() -> Response.status(Response.Status.BAD_REQUEST).entity("Resource %s is not data stream processor").build());
        } finally {
            client.close();
        }
    }

    /**
     * Consumes measurement from remote component.
     * @param measurement A measurement to accept.
     */
    @Consumes(MediaType.APPLICATION_JSON)
    @POST
    @Produces(MediaType.TEXT_PLAIN)
    @Path("/measurement")
    public Response acceptMeasurement(@Context final HttpHeaders headers, final Measurement measurement) {
        //find the appropriate connector and redirect
        final BundleContext context = getBundleContext();
        return ManagedResourceConnectorClient.tryCreate(context, measurement.getInstanceName())
                .map(client -> acceptMeasurement(client, headers, measurement))
                .orElseGet(() -> Response.status(Response.Status.NOT_FOUND).build());
    }

    private void acceptCustomPayload(final Map headers, final Object body) throws Exception {
        payloadDispatcher.handleService(new Payload(headers, body));
    }

    @Consumes({MediaType.TEXT_PLAIN, MediaType.TEXT_HTML})
    @POST
    @Produces({MediaType.TEXT_PLAIN})
    public Response acceptTextPayload(@Context final HttpHeaders headers, final String data){
        try {
            acceptCustomPayload(wrapHeaders(headers), data);
        } catch (final Exception e) {
            throw new WebApplicationException(e, Response.Status.INTERNAL_SERVER_ERROR);
        }
        return Response.noContent().build();
    }

    @Consumes({MediaType.APPLICATION_JSON})
    @POST
    @Produces({MediaType.TEXT_PLAIN})
    public Response acceptJsonPayload(@Context final HttpHeaders headers, final String json){
        try(final StringReader reader = new StringReader(json)) {
            acceptCustomPayload(wrapHeaders(headers), jsonParser.parse(reader));
        } catch (final Exception e) {
            throw new WebApplicationException(e, Response.Status.INTERNAL_SERVER_ERROR);
        }
        return Response.noContent().build();
    }

    @Consumes({MediaType.APPLICATION_XML, MediaType.TEXT_XML})
    @POST
    @Produces({MediaType.TEXT_PLAIN})
    public Response acceptXmlPayload(@Context final HttpHeaders headers, final String xml) {
        try (final StringReader reader = new StringReader(xml)) {
            acceptCustomPayload(wrapHeaders(headers), new XmlSlurperSlim().parse(reader));
        } catch (final SAXException e) {
            throw new WebApplicationException(e, Response.Status.BAD_REQUEST);
        } catch (final Exception e) {
            throw new WebApplicationException(e, Response.Status.INTERNAL_SERVER_ERROR);
        }
        return Response.noContent().build();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy