ATEST-827 and simplify SpringBoot app.

This commit is contained in:
Fabian Märki
2017-10-09 15:34:36 +02:00
parent cbecaa7198
commit 203260dbec
6 changed files with 164 additions and 69 deletions

View File

@@ -1,32 +1,26 @@
package ch.psi.daq.queryrest;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.support.SpringBootServletInitializer;
/**
* Entry point to our rest-frontend of the data acquisition (DAQ) application which most importantly
* wires all the @RestController annotated classes.
*/
@SpringBootApplication
@EnableAutoConfiguration(exclude={
CassandraDataAutoConfiguration.class
})
//@EnableEncryptableProperties
@SpringBootApplication(
exclude = {
CassandraDataAutoConfiguration.class
},
scanBasePackageClasses = {
QueryRestApplication.class
})
//@EnableEncryptableProperties
// http://stackoverflow.com/questions/26655875/spring-boot-redirect-http-to-https
public class QueryRestApplication extends SpringBootServletInitializer {
public class QueryRestApplication {
public static void main(final String[] args) {
System.getProperties().setProperty("hazelcast.phone.home.enabled", "false");
SpringApplication.run(QueryRestApplication.class, args);
}
@Override
protected final SpringApplicationBuilder configure(final SpringApplicationBuilder application) {
return application.sources(QueryRestApplication.class);
}
}

View File

@@ -39,6 +39,7 @@ import ch.psi.daq.domain.query.operation.Aggregation;
import ch.psi.daq.domain.query.operation.QueryField;
import ch.psi.daq.domain.query.operation.aggregation.extrema.AbstractExtremaMeta;
import ch.psi.daq.domain.query.response.Response;
import ch.psi.daq.domain.request.validate.RequestProviderValidator;
import ch.psi.daq.query.analyzer.BackendQueryAnalyzerImpl;
import ch.psi.daq.query.config.QueryConfig;
import ch.psi.daq.queryrest.controller.validator.QueryValidator;
@@ -85,6 +86,7 @@ public class QueryRestConfig { // extends WebMvcConfigurerAdapter {
public static final String BEAN_NAME_QUERY_MANAGER = "queryManager";
public static final String BEAN_NAME_QUERY_ANALIZER_FACTORY = "queryAnalizerFactory";
public static final String BEAN_NAME_QUERY_VALIDATOR = "queryValidator";
public static final String BEAN_NAME_REQUEST_PROVIDER_VALIDATOR = "requestProviderValidator";
public static final String BEAN_NAME_JSON_FACTORY = "jsonFactory";
public static final String BEAN_NAME_MSG_PACK_FACTORY = "msgPackFactory";
public static final String BEAN_NAME_SMILE_FACTORY = "smileFactory";
@@ -126,28 +128,30 @@ public class QueryRestConfig { // extends WebMvcConfigurerAdapter {
objectMapper.addMixIn(Response.class, PolymorphicResponseMixIn.class);
}
// @Override
// public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
// final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
// /**
// * This is necessary so that the message conversion uses the configured object mapper.
// * Otherwise, a separate object mapper is instantiated for Springs message conversion.
// */
// converter.setObjectMapper(objectMapper);
// converters.add(converter);
// super.configureMessageConverters(converters);
// }
// does this work for json comming from web-requests
// -> use WebMvcConfigurationSupport?
// https://stackoverflow.com/questions/26639475/how-to-set-context-param-in-spring-boot
// @Bean
// @Lazy
// public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
// final MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
// jsonConverter.setObjectMapper(objectMapper);
// return jsonConverter;
// }
// @Override
// public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
// final MappingJackson2HttpMessageConverter converter = new
// MappingJackson2HttpMessageConverter();
// /**
// * This is necessary so that the message conversion uses the configured object mapper.
// * Otherwise, a separate object mapper is instantiated for Springs message conversion.
// */
// converter.setObjectMapper(objectMapper);
// converters.add(converter);
// super.configureMessageConverters(converters);
// }
// does this work for json comming from web-requests
// -> use WebMvcConfigurationSupport?
// https://stackoverflow.com/questions/26639475/how-to-set-context-param-in-spring-boot
// @Bean
// @Lazy
// public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
// final MappingJackson2HttpMessageConverter jsonConverter = new
// MappingJackson2HttpMessageConverter();
// jsonConverter.setObjectMapper(objectMapper);
// return jsonConverter;
// }
@Bean(name = BEAN_NAME_JSON_FACTORY)
@Lazy
@@ -267,4 +271,10 @@ public class QueryRestConfig { // extends WebMvcConfigurerAdapter {
public Validator queryValidator() {
return new QueryValidator();
}
@Bean(name = BEAN_NAME_REQUEST_PROVIDER_VALIDATOR)
@Lazy
public Validator requestProviderValidator() {
return new RequestProviderValidator();
}
}

View File

@@ -1,5 +1,7 @@
package ch.psi.daq.queryrest.controller;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
@@ -15,6 +17,9 @@ import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.http.MediaType;
import org.springframework.validation.DirectFieldBindingResult;
import org.springframework.validation.Errors;
import org.springframework.validation.ObjectError;
import org.springframework.validation.Validator;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
@@ -46,7 +51,6 @@ import ch.psi.daq.domain.query.response.Response;
import ch.psi.daq.domain.query.response.ResponseFormat;
import ch.psi.daq.domain.query.transform.image.color.ColorModelType;
import ch.psi.daq.domain.query.transform.image.resize.ValueAggregation;
import ch.psi.daq.domain.request.validate.RequestProviderValidator;
import ch.psi.daq.queryrest.config.QueryRestConfig;
import ch.psi.daq.queryrest.query.QueryManager;
import ch.psi.daq.queryrest.response.AbstractHTTPResponse;
@@ -63,7 +67,7 @@ public class QueryRestController implements ApplicationContextAware {
private ObjectMapper objectMapper;
private QueryManager queryManager;
private Validator queryValidator;
private Validator requestProviderValidator = new RequestProviderValidator();
private Validator requestProviderValidator;
private Response defaultResponse = new JSONHTTPResponse();
@SuppressWarnings("unchecked")
@@ -77,6 +81,7 @@ public class QueryRestController implements ApplicationContextAware {
objectMapper = context.getBean(DomainConfig.BEAN_NAME_OBJECT_MAPPER, ObjectMapper.class);
queryManager = context.getBean(QueryRestConfig.BEAN_NAME_QUERY_MANAGER, QueryManager.class);
queryValidator = context.getBean(QueryRestConfig.BEAN_NAME_QUERY_VALIDATOR, Validator.class);
requestProviderValidator = context.getBean(QueryRestConfig.BEAN_NAME_REQUEST_PROVIDER_VALIDATOR, Validator.class);
}
@InitBinder
@@ -146,8 +151,32 @@ public class QueryRestController implements ApplicationContextAware {
value = DomainConfig.PATH_QUERY,
method = RequestMethod.GET)
public void executeQueryBodyAsString(@RequestParam String jsonBody, HttpServletResponse res) throws Exception {
DAQQuery query = objectMapper.readValue(jsonBody, DAQQuery.class);
executeQuery(query, res);
DAQQuery query;
try {
query = objectMapper.readValue(jsonBody, DAQQuery.class);
} catch (Exception e) {
// somehow only needed for our test mokup environment (@RequestParam seems to do the
// decoding on production server).
LOGGER.info("Could not parse '{}' due to '{}'. Retry url decoded.", jsonBody, e.getMessage());
query = objectMapper.readValue(URLDecoder.decode(jsonBody, StandardCharsets.UTF_8.name()), DAQQuery.class);
}
final Errors errors = new DirectFieldBindingResult(query, query.getClass().getName());
if (requestProviderValidator.supports(query.getClass())) {
requestProviderValidator.validate(query, errors);
}
if (queryValidator.supports(query.getClass())) {
queryValidator.validate(query, errors);
}
final List<ObjectError> allErrors = errors.getAllErrors();
if (allErrors.isEmpty()) {
executeQuery(query, res);
} else {
final String message = String.format("Could not parse '%s' due to '%s'.", jsonBody, errors.toString());
LOGGER.error(message);
throw new IllegalStateException(message);
}
}
/**
@@ -178,8 +207,32 @@ public class QueryRestController implements ApplicationContextAware {
value = DomainConfig.PATH_QUERIES,
method = RequestMethod.GET)
public void executeQueriesBodyAsString(@RequestParam String jsonBody, HttpServletResponse res) throws Exception {
DAQQueries queries = objectMapper.readValue(jsonBody, DAQQueries.class);
executeQueries(queries, res);
DAQQueries queries;
try {
queries = objectMapper.readValue(jsonBody, DAQQueries.class);
} catch (Exception e) {
// somehow only needed for our test mokup environment (@RequestParam seems to do the
// decoding on production server).
LOGGER.info("Could not parse '{}' due to '{}'. Retry url decoded.", jsonBody, e.getMessage());
queries = objectMapper.readValue(URLDecoder.decode(jsonBody, StandardCharsets.UTF_8.name()), DAQQueries.class);
}
final Errors errors = new DirectFieldBindingResult(queries, queries.getClass().getName());
if (requestProviderValidator.supports(queries.getClass())) {
requestProviderValidator.validate(queries, errors);
}
if (queryValidator.supports(queries.getClass())) {
queryValidator.validate(queries, errors);
}
final List<ObjectError> allErrors = errors.getAllErrors();
if (allErrors.isEmpty()) {
executeQueries(queries, res);
} else {
final String message = String.format("Could not parse '%s' due to '%s'.", jsonBody, errors.toString());
LOGGER.error(message);
throw new IllegalStateException(message);
}
}
/**

View File

@@ -12,4 +12,4 @@ filestorage.reader.local=false
filestorage.compaction.startup.init=false
filestorage.startup.repair.init=false
# server.port=8081
#server.port=8081

View File

@@ -15,6 +15,7 @@ import java.util.Set;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVRecord;
import org.apache.http.client.utils.URIBuilder;
import org.junit.After;
import org.junit.Test;
import org.springframework.http.MediaType;
@@ -492,7 +493,7 @@ public class CSVQueryRestControllerTest extends AbstractDaqRestTest {
assertEquals("" + TimeUtils.getMillis(TestTimeUtils.getTimeFromPulseId(pulse)), record.get(column++));
assertEquals("[8]", record.get(column++));
assertEquals("1", record.get(column++));
assertTrue(record.get(column).startsWith("["+pulse+","));
assertTrue(record.get(column).startsWith("[" + pulse + ","));
assertTrue(record.get(column++).endsWith("]"));
}
++pulse;
@@ -1020,6 +1021,11 @@ public class CSVQueryRestControllerTest extends AbstractDaqRestTest {
String response = result.getResponse().getContentAsString();
System.out.println("Response: " + response);
checkDateRangeQueryBinSizeAggregate(channels, aggregations, queryFields, response);
}
private void checkDateRangeQueryBinSizeAggregate(final List<String> channels, final List<Aggregation> aggregations,
final Set<QueryField> queryFields, final String response) throws Exception {
CSVFormat csvFormat = CSVFormat.EXCEL.withDelimiter(CSVResponseStreamWriter.DELIMITER_CVS);
StringReader reader = new StringReader(response);
CSVParser csvParser = new CSVParser(reader, csvFormat);
@@ -1073,6 +1079,61 @@ public class CSVQueryRestControllerTest extends AbstractDaqRestTest {
}
}
@Test
public void testDateRangeQueryBinSizeAggregate_GET() throws Exception {
List<String> channels = Arrays.asList(TEST_CHANNEL_01);
long startTime = 0;
long endTime = 999;
String startDate = TimeUtils.format(startTime);
String endDate = TimeUtils.format(endTime);
List<Aggregation> aggregations = new ArrayList<>();
aggregations.add(Aggregation.min);
aggregations.add(Aggregation.mean);
aggregations.add(Aggregation.max);
DAQQuery request = new DAQQuery(
new RequestRangeDate(
startDate,
endDate),
channels);
// request.setAggregation(new
// AggregationDescriptor().setDurationPerBin(100).setAggregations(aggregations));
// make sure defaults are set
request.setAggregation(new AggregationDescriptor().setDurationPerBin(100));
request.setResponse(new CSVHTTPResponse());
LinkedHashSet<QueryField> queryFields = new LinkedHashSet<>();
queryFields.add(QueryField.channel);
queryFields.add(QueryField.pulseId);
queryFields.add(QueryField.iocSeconds);
queryFields.add(QueryField.iocMillis);
queryFields.add(QueryField.globalSeconds);
queryFields.add(QueryField.globalMillis);
queryFields.add(QueryField.shape);
queryFields.add(QueryField.eventCount);
request.setFields(queryFields);
String content = mapper.writeValueAsString(request);
System.out.println(content);
URIBuilder ub = new URIBuilder(DomainConfig.PATH_QUERY);
ub.addParameter(DomainConfig.PATH_QUERY_PARAM_JSON_BODY, content);
String url = ub.toString();
System.out.println(url);
MvcResult result = this.mockMvc
.perform(MockMvcRequestBuilders
.get(url))
.andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.status().isOk())
.andReturn();
String response = result.getResponse().getContentAsString();
System.out.println("Response: " + response);
checkDateRangeQueryBinSizeAggregate(channels, aggregations, queryFields, response);
}
@Test
public void testQuery_NoTimeFields_01() throws Exception {
List<String> channels = Arrays.asList(TEST_CHANNEL_01, TEST_CHANNEL_02);

View File

@@ -19,17 +19,14 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import ch.psi.daq.domain.backend.Backend;
import ch.psi.daq.domain.config.DomainConfig;
import ch.psi.daq.domain.query.DAQQuery;
import ch.psi.daq.domain.query.operation.Aggregation;
import ch.psi.daq.domain.query.operation.AggregationDescriptor;
import ch.psi.daq.domain.query.operation.Compression;
import ch.psi.daq.domain.query.response.Response;
import ch.psi.daq.domain.request.range.RequestRangeDate;
import ch.psi.daq.domain.request.range.RequestRangePulseId;
import ch.psi.daq.queryrest.response.csv.CSVHTTPResponse;
import ch.psi.daq.queryrest.response.json.JSONHTTPResponse;
import ch.psi.daq.test.queryrest.AbstractDaqRestTest;
public class ResponseQueryTest extends AbstractDaqRestTest{
public class ResponseQueryTest extends AbstractDaqRestTest {
@Resource(name = DomainConfig.BEAN_NAME_BACKEND_DEFAULT)
private Backend backend;
@@ -74,26 +71,6 @@ public class ResponseQueryTest extends AbstractDaqRestTest{
assertEquals(query.getResponse().getCompression().getFileSuffix(), deserial.getResponse().getFileSuffix());
}
@Test
public void test_JSON_02_01() throws JsonParseException, JsonMappingException, IOException {
DAQQuery query = new DAQQuery(
new RequestRangeDate(
"2017-09-25T19:00:05.319+02:00",
"2017-09-25T19:03:05.319+02:00"),
new AggregationDescriptor().setDurationPerBin(100),
"TestChannel_01");
query.setResponse(new CSVHTTPResponse(Compression.GZIP));
// String value = mapper.writeValueAsString(query);
String value = "{\"channels\":[{\"backend\":\"queryrest-1\",\"name\":\"TestChannel_01\"}],\"fields\":[\"globalMillis\",\"globalDate\",\"value\",\"shape\",\"eventCount\"],\"range\":{\"startDate\":\"2017-09-25T19:02:57.318+02:00\",\"endDate\":\"2017-09-25T19:03:05.319+02:00\"},\"aggregation\":{\"durationPerBin\":\"PT15M\"},\"response\":{\"format\":\"csv\",\"compression\":\"none\"}}";
// System.out.println(value);
DAQQuery deserial = mapper.readValue(value, DAQQuery.class);
assertNotNull(deserial.getResponse());
assertEquals(query.getResponse().getClass(), deserial.getResponse().getClass());
assertEquals(query.getResponse().getCompression(), deserial.getResponse().getCompression());
}
@Test
public void test_JSON_03() throws JsonParseException, JsonMappingException, IOException {
DAQQuery query = new DAQQuery(