/* (c) 2016 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.params.extractor;
import org.geoserver.config.GeoServerDataDirectory;
import org.geoserver.platform.GeoServerExtensions;
import org.geoserver.platform.resource.Resource;
import org.geoserver.platform.resource.ResourceStore;
import org.geotools.util.logging.Logging;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamWriter;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.logging.Logger;
import java.util.stream.Collectors;
public final class EchoParametersDao {
private static final Logger LOGGER = Logging.getLogger(EchoParametersDao.class);
private static final String NEW_LINE = System.getProperty("line.separator");
private static final GeoServerDataDirectory DATA_DIRECTORY = (GeoServerDataDirectory) GeoServerExtensions.bean("dataDirectory");
public static String getEchoParametersPath() {
return "params-extractor/echo-parameters.xml";
}
public static String getTmpEchoParametersPath() {
return String.format("params-extractor/%s-echo-parameters.xml", UUID.randomUUID());
}
public static List<EchoParameter> getEchoParameters() {
Resource echoParameters = DATA_DIRECTORY.get(getEchoParametersPath());
return getEchoParameters(echoParameters.in());
}
public static List<EchoParameter> getEchoParameters(InputStream inputStream) {
try {
if (inputStream.available() == 0) {
Utils.debug(LOGGER, "Echo parameters file seems to be empty.");
return new ArrayList<>();
}
EchoParameterHandler handler = new EchoParameterHandler();
SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
saxParser.parse(inputStream, handler);
return handler.echoParameters;
} catch (Exception exception) {
throw Utils.exception(exception, "Error parsing echo parameters files.");
} finally {
Utils.closeQuietly(inputStream);
}
}
public static void saveOrUpdateEchoParameter(EchoParameter echoParameter) {
Resource echoParameters = DATA_DIRECTORY.get(getEchoParametersPath());
Resource tmpEchoParameters = DATA_DIRECTORY.get(getTmpEchoParametersPath());
saveOrUpdateEchoParameter(echoParameter, echoParameters.in(), tmpEchoParameters.out());
echoParameters.delete();
tmpEchoParameters.renameTo(echoParameters);
}
public static void saveOrUpdateEchoParameter(EchoParameter echoParameter, InputStream inputStream, OutputStream outputStream) {
try {
List<EchoParameter> echoParameters = getEchoParameters(inputStream);
boolean exists = false;
for (int i = 0; i < echoParameters.size() && !exists; i++) {
if (echoParameters.get(i).getId().equals(echoParameter.getId())) {
echoParameters.set(i, echoParameter);
exists = true;
}
}
if (!exists) {
echoParameters.add(echoParameter);
}
writeEchoParameters(echoParameters, outputStream);
} finally {
Utils.closeQuietly(inputStream);
Utils.closeQuietly(outputStream);
}
}
public static void deleteEchoParameters(String... echoParametersIds) {
Resource echoParameters = DATA_DIRECTORY.get(getEchoParametersPath());
Resource tmpEchoParameters = DATA_DIRECTORY.get(getTmpEchoParametersPath());
deleteEchoParameters(echoParameters.in(), tmpEchoParameters.out(), echoParametersIds);
echoParameters.delete();
tmpEchoParameters.renameTo(echoParameters);
}
public static void deleteEchoParameters(InputStream inputStream, OutputStream outputStream, String... forwardParameterIds) {
try {
writeEchoParameters(getEchoParameters(inputStream).stream()
.filter(forwardParameter -> !Arrays.stream(forwardParameterIds)
.anyMatch(forwardParameterId -> forwardParameterId.equals(forwardParameter.getId())))
.collect(Collectors.toList()), outputStream);
} finally {
Utils.closeQuietly(inputStream);
Utils.closeQuietly(outputStream);
}
}
private static void writeEchoParameters(List<EchoParameter> echoParameters, OutputStream outputStream) {
try {
XMLStreamWriter output = XMLOutputFactory.newInstance().
createXMLStreamWriter(new OutputStreamWriter(outputStream, "utf-8"));
output.writeStartDocument();
output.writeCharacters(NEW_LINE);
output.writeStartElement("EchoParameters");
output.writeCharacters(NEW_LINE);
echoParameters.forEach(echoParameter -> writeEchoParameter(echoParameter, output));
output.writeEndElement();
output.writeCharacters(NEW_LINE);
output.writeEndDocument();
output.close();
} catch (Exception exception) {
throw Utils.exception(exception, "Something bad happen when writing echo parameters.");
}
}
private static void writeEchoParameter(EchoParameter echoParameter, XMLStreamWriter output) {
try {
output.writeCharacters(" ");
output.writeStartElement("EchoParameter");
writeAttribute("id", echoParameter.getId(), output);
writeAttribute("parameter", echoParameter.getParameter(), output);
writeAttribute("activated", echoParameter.getActivated(), output);
output.writeEndElement();
output.writeCharacters(NEW_LINE);
} catch (Exception exception) {
throw Utils.exception(exception, "Error writing echo parameter %s.", echoParameter.getId());
}
}
private static <T> void writeAttribute(String name, T value, XMLStreamWriter output) throws Exception {
if (value != null) {
output.writeAttribute(name, value.toString());
}
}
private static final class EchoParameterHandler extends DefaultHandler {
final List<EchoParameter> echoParameters = new ArrayList<>();
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if (!qName.equalsIgnoreCase("EchoParameter")) {
return;
}
Utils.debug(LOGGER, "Start parsing echo parameter.");
EchoParameterBuilder echoParameterBuilder = new EchoParameterBuilder();
getAttribute("id", attributes, echoParameterBuilder::withId);
getAttribute("parameter", attributes, echoParameterBuilder::withParameter);
getAttribute("activated", attributes, compose(Boolean::valueOf, echoParameterBuilder::withActivated));
echoParameters.add(echoParameterBuilder.build());
Utils.debug(LOGGER, "End parsing echo parameter.");
}
private static <T> Consumer<String> compose(Function<String, T> convert, Consumer<T> setter) {
return (value) -> setter.accept(convert.apply(value));
}
private void getAttribute(String attributeName, Attributes attributes, Consumer<String> setter) {
String attributeValue = attributes.getValue(attributeName);
if (attributeValue == null) {
Utils.debug(LOGGER, "Echo parameter attribute %s is NULL.", attributeName);
return;
}
Utils.debug(LOGGER, "Echo parameter attribute %s is %s.", attributeName, attributeValue);
try {
setter.accept(attributeValue);
} catch (Exception exception) {
throw Utils.exception(exception,
"Error setting attribute '%s' with value '%s'.", attributeName, attributeValue);
}
}
}
}