/* (c) 2017 Open Source Geospatial Foundation - all rights reserved
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.monitor.rest;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import org.geoserver.monitor.Monitor;
import org.geoserver.monitor.Query;
import org.geoserver.monitor.Query.Comparison;
import org.geoserver.monitor.Query.SortOrder;
import org.geoserver.monitor.RequestData;
import org.geoserver.monitor.RequestDataVisitor;
import org.geoserver.ows.util.OwsUtils;
import org.geoserver.rest.ResourceNotFoundException;
import org.geoserver.rest.RestBaseController;
import org.geoserver.rest.RestException;
import org.geoserver.rest.wrapper.RestWrapper;
import org.geotools.util.Converters;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping(path = { RestBaseController.ROOT_PATH + "/monitor/requests/{request}",
RestBaseController.ROOT_PATH + "/monitor/requests" })
public class MonitorRequestController extends RestBaseController {
static final String CSV_MEDIATYPE_VALUE = "application/csv";
static final String ZIP_MEDIATYPE_VALUE = "application/zip";
static final String EXCEL_MEDIATYPE_VALUE = "application/vnd.ms-excel";
static final MediaType EXCEL_MEDIATYPE = MediaType.valueOf(EXCEL_MEDIATYPE_VALUE);
static final MediaType ZIP_MEDIATYPE = MediaType.valueOf(ZIP_MEDIATYPE_VALUE);
static final MediaType CSV_MEDIATYPE = MediaType.valueOf(CSV_MEDIATYPE_VALUE);
static SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
Monitor monitor;
@Autowired
public MonitorRequestController(Monitor monitor) {
this.monitor = monitor;
}
String[] getFields(String fields) {
if (fields != null) {
return fields.split(";");
} else {
List<String> props = OwsUtils.getClassProperties(RequestData.class).properties();
props.remove("Class");
props.remove("Body");
props.remove("Error");
return props.toArray(new String[props.size()]);
}
}
@GetMapping(produces = { MediaType.TEXT_HTML_VALUE })
@ResponseBody
protected RestWrapper handleObjectGetHtml(
@PathVariable(name = "request", required = false) String req,
@RequestParam(name = "from", required = false) String from,
@RequestParam(name = "to", required = false) String to,
@RequestParam(name = "filter", required = false) String filter,
@RequestParam(name = "order", required = false) String order,
@RequestParam(name = "offset", required = false) Long offset,
@RequestParam(name = "count", required = false) Long count,
@RequestParam(name = "live", required = false) Boolean live,
@RequestParam(name = "fields", required = false) String fieldsSpec) throws Exception {
MonitorQueryResults results = handleObjectGet(req, from, to, filter, order, offset, count,
live, fieldsSpec);
Object object = results.getResult();
// HTML specific bits
if (object instanceof RequestData) {
return wrapObject((RequestData) object, RequestData.class);
} else {
final List<RequestData> requests = new ArrayList<>();
BaseMonitorConverter.handleRequests(object, new RequestDataVisitor() {
public void visit(RequestData data, Object... aggregates) {
requests.add(data);
}
}, monitor);
return wrapList(requests, RequestData.class);
}
}
/**
* Template method to get a custom template name
*
* @param o The object being serialized.
*/
protected String getTemplateName(Object o) {
if (o instanceof RequestData) {
return "request.html";
} else {
return "requests.html";
}
}
@GetMapping(produces = { CSV_MEDIATYPE_VALUE, EXCEL_MEDIATYPE_VALUE, ZIP_MEDIATYPE_VALUE })
@ResponseBody
protected MonitorQueryResults handleObjectGet(
@PathVariable(name = "request", required = false) String req,
@RequestParam(name = "from", required = false) String from,
@RequestParam(name = "to", required = false) String to,
@RequestParam(name = "filter", required = false) String filter,
@RequestParam(name = "order", required = false) String order,
@RequestParam(name = "offset", required = false) Long offset,
@RequestParam(name = "count", required = false) Long count,
@RequestParam(name = "live", required = false) Boolean live,
@RequestParam(name = "fields", required = false) String fieldsSpec) throws Exception {
String[] fields = getFields(fieldsSpec);
if (req == null) {
Query q = new Query().between(from != null ? parseDate(from) : null,
to != null ? parseDate(to) : null);
// filter
if (filter != null) {
try {
parseFilter(filter, q);
} catch (Exception e) {
throw new RestException("Error parsing filter " + filter,
HttpStatus.BAD_REQUEST, e);
}
}
// sorting
String sortBy;
SortOrder sortOrder;
if (order != null) {
int semi = order.indexOf(';');
if (semi != -1) {
String[] split = order.split(";");
sortBy = split[0];
sortOrder = SortOrder.valueOf(split[1]);
} else {
sortBy = order;
sortOrder = SortOrder.ASC;
}
q.sort(sortBy, sortOrder);
}
// limit offset
q.page(offset, count);
// live?
if (live != null) {
if (live) {
q.filter("status",
Arrays.asList(org.geoserver.monitor.RequestData.Status.RUNNING,
org.geoserver.monitor.RequestData.Status.WAITING,
org.geoserver.monitor.RequestData.Status.CANCELLING),
Comparison.IN);
} else {
q.filter("status",
Arrays.asList(org.geoserver.monitor.RequestData.Status.FINISHED,
org.geoserver.monitor.RequestData.Status.FAILED),
Comparison.IN);
}
}
return new MonitorQueryResults(q, fields, monitor);
} else {
// return the individual
RequestData data = monitor.getDAO().getRequest(Long.parseLong(req));
if (data == null) {
throw new ResourceNotFoundException("No such request" + req);
}
return new MonitorQueryResults(data, fields, monitor);
}
}
Date parseDate(String s) {
try {
return DATE_FORMAT.parse(s);
} catch (ParseException e) {
return Converters.convert(s, Date.class);
}
}
void parseFilter(String filter, Query q) {
for (String s : filter.split(";")) {
if ("".equals(s.trim()))
continue;
String[] split = s.split(":");
String left = split[0];
Object right = split[2];
if (right.toString().contains(",")) {
List list = new ArrayList();
for (String t : right.toString().split(",")) {
list.add(parseProperty(left, t));
}
right = list;
} else {
right = parseProperty(left, right.toString());
}
q.and(left, right, Comparison.valueOf(split[1]));
}
}
Object parseProperty(String property, String value) {
if ("status".equals(property)) {
return org.geoserver.monitor.RequestData.Status.valueOf(value);
}
return value;
}
@DeleteMapping
public void handleObjectDelete(@PathVariable(name = "request", required = false) String req) {
if (req == null) {
monitor.getDAO().clear();
} else {
throw new RestException("Cannot delete a specific request", HttpStatus.METHOD_NOT_ALLOWED);
}
}
}