/**
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @author Arne Kepp / The Open Planning Project 2008
*/
package org.geowebcache.rest.seed;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.text.NumberFormat;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import org.geowebcache.GeoWebCacheException;
import org.geowebcache.filter.parameters.FloatParameterFilter;
import org.geowebcache.filter.parameters.ParameterFilter;
import org.geowebcache.filter.parameters.RegexParameterFilter;
import org.geowebcache.filter.parameters.StringParameterFilter;
import org.geowebcache.grid.BoundingBox;
import org.geowebcache.grid.GridSubset;
import org.geowebcache.layer.TileLayer;
import org.geowebcache.mime.ImageMime;
import org.geowebcache.mime.MimeType;
import org.geowebcache.rest.GWCRestlet;
import org.geowebcache.rest.RestletException;
import org.geowebcache.seed.GWCTask;
import org.geowebcache.seed.GWCTask.STATE;
import org.geowebcache.seed.GWCTask.TYPE;
import org.geowebcache.seed.SeedRequest;
import org.geowebcache.seed.TileBreeder;
import org.geowebcache.storage.TileRange;
import org.geowebcache.util.ServletUtils;
import org.restlet.data.Form;
import org.restlet.data.MediaType;
import org.restlet.data.Method;
import org.restlet.data.Request;
import org.restlet.data.Response;
import org.restlet.data.Status;
import org.springframework.util.Assert;
/**
*
*/
public class SeedFormRestlet extends GWCRestlet {
// private static Log log = LogFactory.getLog(org.geowebcache.rest.seed.SeedFormRestlet.class);
private TileBreeder seeder;
public void handle(Request request, Response response) {
Method met = request.getMethod();
try {
if (met.equals(Method.GET)) {
doGet(request, response);
} else if (met.equals(Method.POST)) {
try {
doPost(request, response);
} catch (GeoWebCacheException e) {
throw new RestletException(e.getMessage(), Status.CLIENT_ERROR_BAD_REQUEST);
}
} else {
throw new RestletException("Method not allowed",
Status.CLIENT_ERROR_METHOD_NOT_ALLOWED);
}
} catch (RestletException re) {
response.setEntity(re.getRepresentation());
response.setStatus(re.getStatus());
}
}
public void doGet(Request request, Response response) throws RestletException {
// String layerName = (String) request.getAttributes().get("layer");
String layerName = null;
try {
layerName = URLDecoder.decode((String) request.getAttributes().get("layer"), "UTF-8");
} catch (UnsupportedEncodingException uee) {
}
TileLayer tl;
try {
tl = seeder.findTileLayer(layerName);
} catch (GeoWebCacheException e) {
throw new RestletException(e.getMessage(), Status.CLIENT_ERROR_BAD_REQUEST);
}
handleDoGet(response, tl, false);
}
private void handleDoGet(Response response, TileLayer tl, boolean listAllTasks) {
response.setEntity(makeFormPage(tl, listAllTasks), MediaType.TEXT_HTML);
}
public void doPost(Request req, Response resp) throws RestletException, GeoWebCacheException {
final TileLayer tl;
{
String layerName = null;
if (req.getAttributes().containsKey("layer")) {
try {
layerName = URLDecoder.decode((String) req.getAttributes().get("layer"),
"UTF-8");
} catch (UnsupportedEncodingException uee) {
throw new RuntimeException(uee);
}
try {
tl = seeder.findTileLayer(layerName);
} catch (GeoWebCacheException e) {
throw new RestletException(e.getMessage(), Status.CLIENT_ERROR_BAD_REQUEST);
}
} else {
tl = null;
}
}
Form form = req.getEntityAsForm();
if (form == null) {
throw new RestletException("Unable to parse form result.",
Status.CLIENT_ERROR_BAD_REQUEST);
}
if (form.getFirst("list") != null) {
if (tl == null) {
throw new RestletException("No layer specified", Status.CLIENT_ERROR_BAD_REQUEST);
}
boolean listAllTasks = "all".equals(form.getFirst("list").getValue());
handleDoGet(resp, tl, listAllTasks);
} else if (form.getFirst("kill_thread") != null) {
handleKillThreadPost(form, tl, resp);
} else if (form.getFirst("kill_all") != null) {
handleKillAllThreadsPost(form, tl, resp);
} else if (form.getFirst("minX") != null) {
if (tl == null) {
throw new RestletException("No layer specified", Status.CLIENT_ERROR_BAD_REQUEST);
}
handleDoSeedPost(form, tl, resp);
} else {
throw new RestletException(
"Unknown or malformed request. Please try again, somtimes the form "
+ "is not properly received. This frequently happens on the first POST "
+ "after a restart. The POST was to " + req.getResourceRef().getPath(),
Status.CLIENT_ERROR_BAD_REQUEST);
}
}
private String makeFormPage(TileLayer tl, boolean listAllTasks) {
StringBuilder doc = new StringBuilder();
makeHeader(doc);
makeTaskList(doc, tl, listAllTasks);
makeWarningsAndHints(doc, tl);
makeFormHeader(doc, tl);
makeThreadCountPullDown(doc);
makeTypePullDown(doc);
makeGridSetPulldown(doc, tl);
makeFormatPullDown(doc, tl);
makeZoomStartPullDown(doc, tl);
makeZoomStopPullDown(doc, tl);
makeModifiableParameters(doc, tl);
makeBboxFields(doc);
makeSubmit(doc);
makeFormFooter(doc);
makeFooter(doc);
return doc.toString();
}
private void makeModifiableParameters(StringBuilder doc, TileLayer tl) {
List<ParameterFilter> parameterFilters = tl.getParameterFilters();
if (parameterFilters == null || parameterFilters.size() == 0) {
return;
}
doc.append("<tr><td>Modifiable Parameters:</td><td>\n");
doc.append("<table>");
for (ParameterFilter pf : parameterFilters) {
Assert.notNull(pf);
String key = pf.getKey();
String defaultValue = pf.getDefaultValue();
List<String> legalValues = pf.getLegalValues();
doc.append("<tr><td>").append(key.toUpperCase()).append(": ").append("</td><td>");
String parameterId = "parameter_" + key;
if (pf instanceof StringParameterFilter) {
Map<String, String> keysValues = makeParametersMap(defaultValue, legalValues);
makePullDown(doc, parameterId, keysValues, defaultValue);
} else if (pf instanceof RegexParameterFilter) {
makeTextInput(doc, parameterId, 25);
} else if (pf instanceof FloatParameterFilter) {
FloatParameterFilter floatFilter = (FloatParameterFilter) pf;
if (floatFilter.getValues().isEmpty()) {
// accepts any value
makeTextInput(doc, parameterId, 25);
} else {
Map<String, String> keysValues = makeParametersMap(defaultValue, legalValues);
makePullDown(doc, parameterId, keysValues, defaultValue);
}
} else if ("org.geowebcache.filter.parameters.NaiveWMSDimensionFilter".equals(pf
.getClass().getName())) {
makeTextInput(doc, parameterId, 25);
} else {
// Unknown filter type
if (legalValues == null) {
// Doesn't have a defined set of values, just provide a text field
makeTextInput(doc, parameterId, 25);
} else {
// Does have a defined set of values, so provide a drop down
Map<String, String> keysValues = makeParametersMap(defaultValue, legalValues);
makePullDown(doc, parameterId, keysValues, defaultValue);
}
}
doc.append("</td></tr>");
}
doc.append("</table>");
doc.append("</td></tr>\n");
}
private Map<String, String> makeParametersMap(String defaultValue, List<String> legalValues) {
Map<String, String> map = new TreeMap<String, String>();
for (String s : legalValues) {
map.put(s, s);
}
map.put(defaultValue, defaultValue);
return map;
}
private String makeResponsePage(TileLayer tl) {
StringBuilder doc = new StringBuilder();
makeHeader(doc);
doc.append("<h3>Task submitted</h3>\n");
doc.append("<p>Below you can find a list of currently executing tasks, take the numbers with a grain of salt");
doc.append(" until the task has had a chance to run for a few minutes. ");
makeTaskList(doc, tl, false);
makeFooter(doc);
return doc.toString();
}
private void makeTypePullDown(StringBuilder doc) {
doc.append("<tr><td>Type of operation:</td><td>\n");
Map<String, String> keysValues = new TreeMap<String, String>();
keysValues.put("Truncate - remove tiles", "truncate");
keysValues.put("Seed - generate missing tiles", "seed");
keysValues.put("Reseed - regenerate all tiles", "reseed");
makePullDown(doc, "type", keysValues, "Seed - generate missing tiles");
doc.append("</td></tr>\n");
}
private void makeThreadCountPullDown(StringBuilder doc) {
doc.append("<tr><td>Number of tasks to use:</td><td>\n");
Map<String, String> keysValues = new TreeMap<String, String>();
for (int i = 1; i < 17; i++) {
if (i < 10) {
keysValues.put("0" + Integer.toString(i), "0" + Integer.toString(i));
} else {
keysValues.put(Integer.toString(i), Integer.toString(i));
}
}
makePullDown(doc, "threadCount", keysValues, Integer.toString(2));
doc.append("</td></tr>\n");
}
private void makeBboxFields(StringBuilder doc) {
doc.append("<tr><td valign=\"top\">Bounding box:</td><td>\n");
makeTextInput(doc, "minX", 6);
makeTextInput(doc, "minY", 6);
makeTextInput(doc, "maxX", 6);
makeTextInput(doc, "maxY", 6);
doc.append("</br>These are optional, approximate values are fine.");
doc.append("</td></tr>\n");
}
private void makeBboxHints(StringBuilder doc, TileLayer tl) {
for (String gridSetId : tl.getGridSubsets()) {
GridSubset subset = tl.getGridSubset(gridSetId);
doc.append("<li>" + gridSetId + ": " + subset.getOriginalExtent().toString()
+ "</li>\n");
}
}
private void makeTextInput(StringBuilder doc, String id, int size) {
doc.append("<input name=\"" + id + "\" type=\"text\" size=\"" + size + "\" />\n");
}
private void makeSubmit(StringBuilder doc) {
doc.append("<tr><td></td><td><input type=\"submit\" value=\"Submit\"></td></tr>\n");
}
private void makeZoomStopPullDown(StringBuilder doc, TileLayer tl) {
doc.append("<tr><td>Zoom stop:</td><td>\n");
makeZoomPullDown(doc, false, tl);
doc.append("</td></tr>\n");
}
private void makeZoomStartPullDown(StringBuilder doc, TileLayer tl) {
doc.append("<tr><td>Zoom start:</td><td>\n");
makeZoomPullDown(doc, true, tl);
doc.append("</td></tr>\n");
}
private void makeZoomPullDown(StringBuilder doc, boolean isStart, TileLayer tl) {
Map<String, String> keysValues = new TreeMap<String, String>();
int minStart = Integer.MAX_VALUE;
int maxStop = Integer.MIN_VALUE;
for (String gridSetId : tl.getGridSubsets()) {
GridSubset subset = tl.getGridSubset(gridSetId);
int start = subset.getZoomStart();
int stop = subset.getZoomStop();
if (start < minStart) {
minStart = start;
}
if (stop > maxStop) {
maxStop = stop;
}
}
for (int i = minStart; i <= maxStop; i++) {
if (i < 10) {
keysValues.put("0" + Integer.toString(i), "0" + Integer.toString(i));
} else {
keysValues.put(Integer.toString(i), Integer.toString(i));
}
}
if (isStart) {
if (minStart < 10) {
makePullDown(doc, "zoomStart", keysValues, "0" + Integer.toString(minStart));
} else {
makePullDown(doc, "zoomStart", keysValues, Integer.toString(minStart));
}
} else {
int midStop = (minStart + maxStop) / 2;
if (midStop < 10) {
makePullDown(doc, "zoomStop", keysValues, "0" + Integer.toString(midStop));
} else {
makePullDown(doc, "zoomStop", keysValues, Integer.toString(midStop));
}
}
}
private void makeFormatPullDown(StringBuilder doc, TileLayer tl) {
doc.append("<tr><td>Format:</td><td>\n");
Map<String, String> keysValues = new TreeMap<String, String>();
Iterator<MimeType> iter = tl.getMimeTypes().iterator();
while (iter.hasNext()) {
MimeType mime = iter.next();
keysValues.put(mime.getFormat(), mime.getFormat());
}
makePullDown(doc, "format", keysValues, ImageMime.png.getFormat());
doc.append("</td></tr>\n");
}
private void makeGridSetPulldown(StringBuilder doc, TileLayer tl) {
doc.append("<tr><td>Grid Set:</td><td>\n");
Map<String, String> keysValues = new TreeMap<String, String>();
String firstGridSetId = null;
for (String gridSetId : tl.getGridSubsets()) {
if (firstGridSetId == null) {
firstGridSetId = gridSetId;
}
keysValues.put(gridSetId, gridSetId);
}
makePullDown(doc, "gridSetId", keysValues, firstGridSetId);
doc.append("</td></tr>\n");
}
private void makePullDown(StringBuilder doc, String id, Map<String, String> keysValues,
String defaultKey) {
doc.append("<select name=\"" + id + "\">\n");
Iterator<Entry<String, String>> iter = keysValues.entrySet().iterator();
while (iter.hasNext()) {
Entry<String, String> entry = iter.next();
if (entry.getKey().equals(defaultKey)) {
doc.append("<option value=\"" + entry.getValue() + "\" selected=\"selected\">"
+ entry.getKey() + "</option>\n");
} else {
doc.append("<option value=\"" + entry.getValue() + "\">" + entry.getKey()
+ "</option>\n");
}
}
doc.append("</select>\n");
}
private void makeFormHeader(StringBuilder doc, TileLayer tl) {
doc.append("<h4>Create a new task:</h4>\n");
doc.append("<form id=\"seed\" action=\"./" + tl.getName() + "\" method=\"post\">\n");
doc.append("<table border=\"0\" cellspacing=\"10\">\n");
}
private void makeFormFooter(StringBuilder doc) {
doc.append("</table>\n");
doc.append("</form>\n");
}
private void makeHeader(StringBuilder doc) {
doc.append("<html>\n" + ServletUtils.gwcHtmlHeader("GWC Seed Form") + "<body>\n"
+ ServletUtils.gwcHtmlLogoLink("../../"));
}
private void makeWarningsAndHints(StringBuilder doc, TileLayer tl) {
doc.append("<h4>Please note:</h4><ul>\n"
+ "<li>This minimalistic interface does not check for correctness.</li>\n"
+ "<li>Seeding past zoomlevel 20 is usually not recommended.</li>\n"
+ "<li>Truncating KML will also truncate all KMZ archives.</li>\n"
+ "<li>Please check the logs of the container to look for error messages and progress indicators.</li>\n"
+ "</ul>\n");
doc.append("Here are the max bounds, if you do not specify bounds these will be used.\n");
doc.append("<ul>\n");
makeBboxHints(doc, tl);
doc.append("</ul>\n");
}
private void makeTaskList(StringBuilder doc, TileLayer tl, boolean listAll) {
doc.append(makeKillallThreadsForm(tl, listAll));
doc.append("<h4>List of currently executing tasks:</h4>\n");
Iterator<GWCTask> iter = seeder.getRunningAndPendingTasks();
boolean tasks = false;
if (!iter.hasNext()) {
doc.append("<ul><li><i>none</i></li></ul>\n");
} else {
doc.append("<table border=\"0\">");
doc.append("<tr style=\"font-weight: bold;\"><td style=\"padding-right:20px;\">Id</td><td style=\"padding-right:20px;\">Layer</td><td style=\"padding-right:20px;\">Status</td><td style=\"padding-right:20px;\">Type</td><td>Estimated # of tiles</td>"
+ "<td style=\"padding-right:20px;\">Tiles completed</td><td style=\"padding-right:20px;\">Time elapsed</td><td>Time remaining</td><td>Tasks</td><td> </td>");
doc.append("</tr>");
tasks = true;
}
int row = 0;
final String layerName = tl.getName();
while (iter.hasNext()) {
GWCTask task = iter.next();
if (!listAll && !layerName.equals(task.getLayerName())) {
continue;
}
final long spent = task.getTimeSpent();
final long remining = task.getTimeRemaining();
final long tilesDone = task.getTilesDone();
final long tilesTotal = task.getTilesTotal();
NumberFormat nf = NumberFormat.getInstance(Locale.ENGLISH);
nf.setGroupingUsed(true);
final String tilesTotalStr;
if (tilesTotal < 0) {
tilesTotalStr = "Too many to count";
} else {
tilesTotalStr = nf.format(tilesTotal);
}
final String tilesDoneStr = nf.format(task.getTilesDone());
final STATE state = task.getState();
final String status = STATE.UNSET.equals(state) || STATE.READY.equals(state) ? "PENDING"
: state.toString();
String timeSpent = toTimeString(spent, tilesDone, tilesTotal);
String timeRemaining = toTimeString(remining, tilesDone, tilesTotal);
String bgColor = ++row % 2 == 0 ? "#FFFFFF" : "#DDDDDD";
doc.append("<tr style=\"background-color:" + bgColor + ";\">");
doc.append("<td style=\"text-align:right\">").append(task.getTaskId()).append("</td>");
doc.append("<td>");
if (!layerName.equals(task.getLayerName())) {
doc.append("<a href=\"./").append(task.getLayerName()).append("\">");
}
doc.append(task.getLayerName());
if (!layerName.equals(task.getLayerName())) {
doc.append("</a>");
}
doc.append("</td>");
doc.append("<td>").append(status).append("</td>");
doc.append("<td>").append(task.getType()).append("</td>");
doc.append("<td>").append(tilesTotalStr).append("</td>");
doc.append("<td>").append(tilesDoneStr).append("</td>");
doc.append("<td>").append(timeSpent).append("</td>");
doc.append("<td>").append(timeRemaining).append("</td>");
doc.append("<td>(Task ").append(task.getThreadOffset() + 1).append(" of ")
.append(task.getThreadCount()).append(") </td>");
doc.append("<td>").append(makeThreadKillForm(task.getTaskId(), tl)).append("</td>");
doc.append("<tr>");
}
if (tasks) {
doc.append("</table>");
}
doc.append("<p><a href=\"./" + layerName + "\">Refresh list</a></p>\n");
}
private String toTimeString(long timeSeconds, final long tilesDone, final long tilesTotal) {
String timeString;
if (tilesDone < 50) {
timeString = " Estimating...";
} else {
final int MINUTE_SECONDS = 60;
final int HOUR_SECONDS = MINUTE_SECONDS * 60;
final int DAY_SECONDS = HOUR_SECONDS * 24;
if (timeSeconds == -2 && tilesDone < tilesTotal) {
timeString = " A decade or three.";
} else {
if (timeSeconds > DAY_SECONDS) {
timeString = (timeSeconds / DAY_SECONDS) + " day(s) ";
timeString += ((timeSeconds % DAY_SECONDS) / HOUR_SECONDS) + "h)";
} else if (timeSeconds > HOUR_SECONDS) {
long hours = timeSeconds / HOUR_SECONDS;
long minutes = (timeSeconds % HOUR_SECONDS) / MINUTE_SECONDS;
timeString = hours + " hour" + (hours > 1 ? "s " : " ");
timeString += minutes == 0 ? "" : (minutes + " m");
} else if (timeSeconds > MINUTE_SECONDS) {
long minutes = timeSeconds / MINUTE_SECONDS;
long seconds = timeSeconds % MINUTE_SECONDS;
timeString = minutes + " minute" + (minutes > 1 ? "s " : " ");
timeString += seconds == 0 ? "" : seconds + " s";
} else {
timeString = timeSeconds + " second" + (timeSeconds == 1 ? "" : "s");
}
}
}
return timeString;
}
private String makeThreadKillForm(Long key, TileLayer tl) {
String ret = "<form form id=\"kill\" action=\"./"
+ tl.getName()
+ "\" method=\"post\">"
+ "<input type=\"hidden\" name=\"kill_thread\" value=\"1\" />"
+ "<input type=\"hidden\" name=\"thread_id\" value=\""
+ key
+ "\" />"
+ "<span><input style=\"padding: 0; margin-bottom: -12px; border: 1;\"type=\"submit\" value=\"Kill Task\"></span>"
+ "</form>";
return ret;
}
private String makeKillallThreadsForm(TileLayer tl, boolean listAll) {
StringBuilder doc = new StringBuilder();
final String layerName = tl.getName();
int otherLayersTaskCount = 0;
if (!listAll) {
Iterator<GWCTask> tasks = seeder.getRunningAndPendingTasks();
while (tasks.hasNext()) {
if (!layerName.equals(tasks.next().getLayerName())) {
otherLayersTaskCount++;
}
}
}
doc.append("<table><tr><td>");
doc.append("<form form id=\"list\" action=\"./").append(layerName)
.append("\" method=\"post\">\n");
doc.append("List ");
doc.append("<select name=\"list\" onchange=\"this.form.submit();\">\n");
doc.append("<option value=\"layer\"").append(listAll ? "" : " selected")
.append(">this Layer tasks</option>\n");
doc.append("<option value=\"all\"").append(listAll ? " selected" : "")
.append(">all Layers tasks</option>\n");
doc.append("</select>\n");
if (!listAll) {
doc.append(" (there are ");
if (otherLayersTaskCount > 0) {
doc.append(otherLayersTaskCount);
} else {
doc.append("no");
}
doc.append(" tasks for other Layers)");
}
doc.append("</form>\n");
doc.append("</td></tr><tr><td>");
doc.append("<form form id=\"kill\" action=\"./").append(layerName)
.append("\" method=\"post\">\n");
doc.append("<span>Kill \n");
doc.append("<select name=\"kill_all\">\n");
doc.append("<option value=\"all\">all</option>\n");
doc.append("<option value=\"running\">running</option>\n");
doc.append("<option value=\"pending\">pending</option>\n");
doc.append("</select>\n");
doc.append(" Tasks for Layer '").append(layerName).append("'.");
doc.append("<input type=\"submit\" value=\" Submit\">");
doc.append("</span>\n");
doc.append("</form>\n");
doc.append("</td></tr></table>");
return doc.toString();
}
private void makeFooter(StringBuilder doc) {
doc.append("</body></html>\n");
}
private void handleKillAllThreadsPost(Form form, TileLayer tl, Response resp)
throws RestletException {
final boolean allLayers = tl == null;
String killCode = form.getFirst("kill_all").getValue();
final Iterator<GWCTask> tasks;
if ("1".equals(killCode) || "running".equalsIgnoreCase(killCode)) {
killCode = "running";
tasks = seeder.getRunningTasks();
} else if ("pending".equalsIgnoreCase(killCode)) {
tasks = seeder.getPendingTasks();
} else if ("all".equalsIgnoreCase(killCode)) {
tasks = seeder.getRunningAndPendingTasks();
} else {
throw new RestletException("Unknown kill_all code: '" + killCode
+ "'. One of all|running|pending is expected.", Status.CLIENT_ERROR_BAD_REQUEST);
}
List<GWCTask> terminatedTasks = new LinkedList<GWCTask>();
List<GWCTask> nonTerminatedTasks = new LinkedList<GWCTask>();
while (tasks.hasNext()) {
GWCTask task = tasks.next();
String layerName = task.getLayerName();
if (!allLayers && !tl.getName().equals(layerName)) {
continue;
}
long taskId = task.getTaskId();
boolean terminated = seeder.terminateGWCTask(taskId);
if (terminated) {
terminatedTasks.add(task);
} else {
nonTerminatedTasks.add(task);
}
}
StringBuilder doc = new StringBuilder();
makeHeader(doc);
doc.append("<p>Requested to terminate ").append(killCode).append(" tasks.");
doc.append("Terminated tasks: <ul>");
for (GWCTask t : terminatedTasks) {
doc.append("<li>").append(t).append("</li>");
}
doc.append("</ul>Tasks already finished: <ul>");
for (GWCTask t : nonTerminatedTasks) {
doc.append("<li>").append(t).append("</li>");
}
if (tl != null) {
doc.append("</ul><p><a href=\"./" + tl.getName() + "\">Go back</a></p>\n");
}
resp.setEntity(doc.toString(), MediaType.TEXT_HTML);
}
private void handleKillThreadPost(Form form, TileLayer tl, Response resp) {
String id = form.getFirstValue("thread_id");
StringBuilder doc = new StringBuilder();
makeHeader(doc);
if (seeder.terminateGWCTask(Long.parseLong(id))) {
doc.append("<ul><li>Requested to terminate task " + id + ".</li></ul>");
} else {
doc.append("<ul><li>Sorry, either task "
+ id
+ " has not started yet, or it is a truncate task that cannot be interrutped.</li></ul>");
;
}
if (tl != null) {
doc.append("<p><a href=\"./" + tl.getName() + "\">Go back</a></p>\n");
}
resp.setEntity(doc.toString(), MediaType.TEXT_HTML);
}
private void handleDoSeedPost(Form form, TileLayer tl, Response resp) throws RestletException,
GeoWebCacheException {
BoundingBox bounds = null;
if (form.getFirst("minX").getValue() != null) {
bounds = new BoundingBox(parseDouble(form, "minX"), parseDouble(form, "minY"),
parseDouble(form, "maxX"), parseDouble(form, "maxY"));
}
String gridSetId = form.getFirst("gridSetId").getValue();
int threadCount = Integer.parseInt(form.getFirst("threadCount").getValue());
int zoomStart = Integer.parseInt(form.getFirst("zoomStart").getValue());
int zoomStop = Integer.parseInt(form.getFirst("zoomStop").getValue());
String format = form.getFirst("format").getValue();
Map<String, String> fullParameters;
{
Map<String, String> parameters = new HashMap<String, String>();
Set<String> paramNames = form.getNames();
String prefix = "parameter_";
for (String name : paramNames) {
if (name.startsWith(prefix)) {
String paramName = name.substring(prefix.length());
String value = form.getFirstValue(name);
parameters.put(paramName, value);
}
}
fullParameters = tl.getModifiableParameters(parameters, "UTF-8");
}
TYPE type = GWCTask.TYPE.valueOf(form.getFirst("type").getValue().toUpperCase());
final String layerName = tl.getName();
SeedRequest sr = new SeedRequest(layerName, bounds, gridSetId, threadCount, zoomStart,
zoomStop, format, type, fullParameters);
TileRange tr = TileBreeder.createTileRange(sr, tl);
GWCTask[] tasks;
try {
tasks = seeder.createTasks(tr, tl, sr.getType(), sr.getThreadCount(),
sr.getFilterUpdate());
} catch (GeoWebCacheException e) {
throw new RestletException(e.getMessage(), Status.SERVER_ERROR_INTERNAL);
}
seeder.dispatchTasks(tasks);
// Give the thread executor a chance to run
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// Ok, no worries
}
resp.setEntity(this.makeResponsePage(tl), MediaType.TEXT_HTML);
}
private static double parseDouble(Form form, String key) throws RestletException {
String value = form.getFirst(key).getValue();
if (value == null || value.length() == 0)
throw new RestletException("Missing value for " + key, Status.CLIENT_ERROR_BAD_REQUEST);
try {
return Double.parseDouble(value);
} catch (NumberFormatException nfe) {
throw new RestletException("Value for " + key + " is not a double",
Status.CLIENT_ERROR_BAD_REQUEST);
}
}
public void setTileBreeder(TileBreeder seeder) {
this.seeder = seeder;
}
}