/* (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.rest.catalog;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.geoserver.ManifestLoader;
import org.geoserver.ManifestLoader.AboutModel;
import org.geoserver.ManifestLoader.AboutModel.AboutModelType;
import org.geoserver.ManifestLoader.AboutModel.ManifestModel;
import org.geoserver.config.util.XStreamPersister;
import org.geoserver.rest.ObjectToMapWrapper;
import org.geoserver.rest.RestBaseController;
import org.geoserver.rest.converters.XStreamMessageConverter;
import org.geoserver.rest.wrapper.RestWrapper;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import freemarker.template.ObjectWrapper;
import freemarker.template.SimpleHash;
@RestController
@RequestMapping(path = RestBaseController.ROOT_PATH + "/about",
produces = { MediaType.TEXT_HTML_VALUE, MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE })
@ControllerAdvice
public class AboutController extends RestBaseController {
@GetMapping(value = "/manifest")
public RestWrapper<AboutModel> manifestGet(
@RequestParam(name = "manifest", required = false) String regex,
@RequestParam(required = false) String from, @RequestParam(required = false) String to,
@RequestParam(required = false) String key,
@RequestParam(required = false) String value) {
return wrapObject(getModel(AboutModelType.RESOURCES, regex, from, to, key, value),
AboutModel.class);
}
@GetMapping(value = "/version")
public RestWrapper<AboutModel> versionGet(
@RequestParam(name = "manifest", required = false) String regex,
@RequestParam(required = false) String from, @RequestParam(required = false) String to,
@RequestParam(required = false) String key,
@RequestParam(required = false) String value) {
return wrapObject(getModel(AboutModelType.VERSIONS, regex, from, to, key, value),
AboutModel.class);
}
protected AboutModel getModel(AboutModelType type, String regex, String from, String to,
String key, String value) {
AboutModel model = null;
// filter name by regex
if (regex != null) {
model = buildAboutModel(type).filterNameByRegex(regex);
}
// filter name by range
if (from != null && to != null) {
if (model != null) {
model = model.filterNameByRange(from, to);
} else {
model = buildAboutModel(type).filterNameByRange(from, to);
}
}
// filter by properties
if (model == null) {
model = buildAboutModel(type);
}
if (key != null && value != null) {
model = model.filterPropertyByKeyValue(value, key);
} else if (key != null) {
model = model.filterPropertyByKey(key);
} else if (value != null) {
model = model.filterPropertyByValue(value);
}
if (model != null) {
return model;
} else {
return buildAboutModel(type);
}
}
private static AboutModel buildAboutModel(AboutModelType type) {
if (type.equals(AboutModelType.RESOURCES)) {
// if request is for resource return the resources
return ManifestLoader.getResources();
} else {
// get the version
return ManifestLoader.getVersions();
}
}
@Override
protected String getTemplateName(Object object) {
if (object instanceof AboutModel) {
return "AboutModel.ftl";
} else {
return null;
}
}
@Override
public boolean supports(MethodParameter methodParameter, Type targetType,
Class<? extends HttpMessageConverter<?>> converterType) {
return AboutModel.class.isAssignableFrom(methodParameter.getParameterType());
}
@Override
protected <T> ObjectWrapper createObjectWrapper(Class<T> clazz) {
if (AboutModel.class.isAssignableFrom(clazz)) {
return new ObjectToMapWrapper<AboutModel>(AboutModel.class) {
@Override
protected void wrapInternal(Map properties, SimpleHash model, AboutModel object) {
final List<Map<String, Object>> manifests = new ArrayList<>();
for (ManifestModel manifest : object.getManifests()) {
final Map<String, Object> map = new HashMap<>();
map.put("name", manifest.getName());
final List<String> props = new ArrayList<>();
map.put("properties", props);
final List<String> values = new ArrayList<>();
map.put("valuez", values);
for (String key : manifest.getEntries().keySet()) {
props.add(key);
values.add(manifest.getEntries().get(key));
}
manifests.add(map);
}
properties.put("manifests", manifests);
}
};
} else {
return null;
}
}
public void configurePersister(XStreamPersister persister, XStreamMessageConverter converter) {
XStream xs = persister.getXStream();
// AboutModel
xs.processAnnotations(AboutModel.class);
xs.allowTypes(new Class[] { AboutModel.class });
xs.addImplicitCollection(AboutModel.class, "manifests");
xs.alias("about", AboutModel.class);
// ManifestModel Xstream converter
xs.registerConverter(new Converter() {
@Override
public boolean canConvert(Class type) {
return type.equals(ManifestModel.class);
}
@Override
public void marshal(Object source, HierarchicalStreamWriter writer,
MarshallingContext context) {
ManifestModel model = (ManifestModel) source;
writer.addAttribute("name", model.getName());
for (java.util.Map.Entry<String, String> entry : model.getEntries().entrySet())
context.convertAnother(entry, new Converter() {
@Override
public boolean canConvert(Class type) {
return Entry.class.isAssignableFrom(type);
}
@Override
public void marshal(Object source, HierarchicalStreamWriter writer,
MarshallingContext context) {
@SuppressWarnings("unchecked")
Entry<String, String> e = (Entry<String, String>) source;
writer.startNode(e.getKey());
writer.setValue(e.getValue());
writer.endNode();
}
@Override
public Object unmarshal(HierarchicalStreamReader reader,
UnmarshallingContext context) {
throw new UnsupportedOperationException("Not implemented");
}
});
}
@Override
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
throw new UnsupportedOperationException("Not implemented");
}
});
xs.alias("resource", ManifestModel.class);
xs.addImplicitCollection(ManifestModel.class, "entries");
xs.useAttributeFor(ManifestModel.class, "name");
xs.alias("property", Entry.class);
xs.autodetectAnnotations(true);
}
}