/** * */ package ch.psi.daq.rest; import java.io.IOException; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; import javax.servlet.ServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import ch.psi.daq.domain.cassandra.DataEvent; import ch.psi.daq.rest.queries.AbstractQuery; import com.fasterxml.jackson.core.JsonEncoding; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter; import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider; /** * Takes a Java 8 stream and writes it to the output stream provided by the {@link ServletResponse} * of the current request. */ public class ResponseStreamWriter { private static final Logger logger = LoggerFactory.getLogger(ResponseStreamWriter.class); @Autowired private JsonFactory jsonFactory; @Autowired private ObjectMapper mapper; /** * Responding with the the contents of the stream by writing into the output stream of the * {@link ServletResponse}. * * @param stream {@link Stream} instance of {@link DataEvent}s * @param query concrete instance of {@link AbstractQuery} * @param response {@link ServletResponse} instance given by the current HTTP request * @throws IOException thrown if writing to the output stream fails */ public void respond(Stream stream, AbstractQuery query, ServletResponse response) throws IOException { Set includedFields = query.getFields(); if (query.getAggregations() != null) { includedFields.addAll(query.getAggregations() .stream() .map(a -> { return a.getType().toString(); }) .collect(Collectors.toSet())); } ObjectWriter writer = configureWriter(includedFields); respondInternal(stream, response, writer); } /** * Configures the writer dynamically by including the fields which should be included in the * response. * * @param includedFields set of strings which correspond to the getter method names of the * classes registered as a mixed-in * @return the configured writer that includes the specified fields */ private ObjectWriter configureWriter(Set includedFields) { SimpleFilterProvider propertyFilter = new SimpleFilterProvider(); propertyFilter.addFilter("namedPropertyFilter", SimpleBeanPropertyFilter.filterOutAllExcept(includedFields)); // only write the properties not excluded in the filter ObjectWriter writer = mapper.writer(propertyFilter); return writer; } /** * Writes the Java stream into the output stream. * * @param stream {@link Stream} instance of {@link DataEvent}s * @param response {@link ServletResponse} instance given by the current HTTP request * @param writer configured writer that includes the fields the end user wants to see * @throws IOException thrown if writing to the output stream fails */ private void respondInternal(Stream stream, ServletResponse response, ObjectWriter writer) throws IOException { JsonGenerator generator = jsonFactory.createGenerator(response.getOutputStream(), JsonEncoding.UTF8); generator.writeStartArray(); stream.forEach(ds -> { try { logger.trace("Writing value for: {}", ds); // use the writer created just before writer.writeValue(generator, ds); } catch (Exception e) { logger.error("", e); } }); generator.writeEndArray(); generator.flush(); generator.close(); } }