source: josm/trunk/src/org/openstreetmap/josm/io/remotecontrol/handler/RequestHandler.java@ 6316

Last change on this file since 6316 was 6316, checked in by Don-vip, 11 years ago

Sonar/FindBugs - Loose coupling

File size: 10.3 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.io.remotecontrol.handler;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.io.UnsupportedEncodingException;
7import java.net.URLDecoder;
8import java.text.MessageFormat;
9import java.util.Collections;
10import java.util.HashMap;
11import java.util.HashSet;
12import java.util.LinkedList;
13import java.util.List;
14import java.util.Map;
15
16import javax.swing.JLabel;
17import javax.swing.JOptionPane;
18
19import org.openstreetmap.josm.Main;
20import org.openstreetmap.josm.io.remotecontrol.PermissionPrefWithDefault;
21import org.openstreetmap.josm.tools.Utils;
22
23/**
24 * This is the parent of all classes that handle a specific remote control command
25 *
26 * @author Bodo Meissner
27 */
28public abstract class RequestHandler {
29
30 public static final String globalConfirmationKey = "remotecontrol.always-confirm";
31 public static final boolean globalConfirmationDefault = false;
32 public static final String loadInNewLayerKey = "remotecontrol.new-layer";
33 public static final boolean loadInNewLayerDefault = false;
34
35 /** The GET request arguments */
36 protected Map<String,String> args;
37
38 /** The request URL without "GET". */
39 protected String request;
40
41 /** default response */
42 protected String content = "OK\r\n";
43 /** default content type */
44 protected String contentType = "text/plain";
45
46 /** will be filled with the command assigned to the subclass */
47 protected String myCommand;
48
49 /**
50 * who send th request?
51 * the host from refrerer header or IP of request sender
52 */
53 protected String sender;
54
55 /**
56 * Check permission and parameters and handle request.
57 *
58 * @throws RequestHandlerForbiddenException
59 * @throws RequestHandlerBadRequestException
60 * @throws RequestHandlerErrorException
61 */
62 public final void handle() throws RequestHandlerForbiddenException, RequestHandlerBadRequestException, RequestHandlerErrorException
63 {
64 checkMandatoryParams();
65 validateRequest();
66 checkPermission();
67 handleRequest();
68 }
69
70 /**
71 * Validates the request before attempting to perform it.
72 * @throws RequestHandlerBadRequestException
73 * @since 5678
74 */
75 protected abstract void validateRequest() throws RequestHandlerBadRequestException;
76
77 /**
78 * Handle a specific command sent as remote control.
79 *
80 * This method of the subclass will do the real work.
81 *
82 * @throws RequestHandlerErrorException
83 * @throws RequestHandlerBadRequestException
84 */
85 protected abstract void handleRequest() throws RequestHandlerErrorException, RequestHandlerBadRequestException;
86
87 /**
88 * Get a specific message to ask the user for permission for the operation
89 * requested via remote control.
90 *
91 * This message will be displayed to the user if the preference
92 * remotecontrol.always-confirm is true.
93 *
94 * @return the message
95 */
96 abstract public String getPermissionMessage();
97
98 /**
99 * Get a PermissionPref object containing the name of a special permission
100 * preference to individually allow the requested operation and an error
101 * message to be displayed when a disabled operation is requested.
102 *
103 * Default is not to check any special preference. Override this in a
104 * subclass to define permission preference and error message.
105 *
106 * @return the preference name and error message or null
107 */
108 abstract public PermissionPrefWithDefault getPermissionPref();
109
110 abstract public String[] getMandatoryParams();
111
112 public String[] getOptionalParams() {
113 return null;
114 }
115
116 public String[] getUsageExamples() {
117 return null;
118 }
119
120 /**
121 * Check permissions in preferences and display error message
122 * or ask for permission.
123 *
124 * @throws RequestHandlerForbiddenException
125 */
126 final public void checkPermission() throws RequestHandlerForbiddenException
127 {
128 /*
129 * If the subclass defines a specific preference and if this is set
130 * to false, abort with an error message.
131 *
132 * Note: we use the deprecated class here for compatibility with
133 * older versions of WMSPlugin.
134 */
135 PermissionPrefWithDefault permissionPref = getPermissionPref();
136 if((permissionPref != null) && (permissionPref.pref != null))
137 {
138 if (!Main.pref.getBoolean(permissionPref.pref, permissionPref.defaultVal)) {
139 String err = MessageFormat.format("RemoteControl: ''{0}'' forbidden by preferences", myCommand);
140 Main.info(err);
141 throw new RequestHandlerForbiddenException(err);
142 }
143 }
144
145 /* Does the user want to confirm everything?
146 * If yes, display specific confirmation message.
147 */
148 if (Main.pref.getBoolean(globalConfirmationKey, globalConfirmationDefault)) {
149 // Ensure dialog box does not exceed main window size
150 Integer maxWidth = (int) Math.max(200, Main.parent.getWidth()*0.6);
151 String message = "<html><div>" + getPermissionMessage() +
152 "<br/>" + tr("Do you want to allow this?") + "</div></html>";
153 JLabel label = new JLabel(message);
154 if (label.getPreferredSize().width > maxWidth) {
155 label.setText(message.replaceFirst("<div>", "<div style=\"width:" + maxWidth + "px;\">"));
156 }
157 if (JOptionPane.showConfirmDialog(Main.parent, label,
158 tr("Confirm Remote Control action"),
159 JOptionPane.YES_NO_OPTION) != JOptionPane.YES_OPTION) {
160 String err = MessageFormat.format("RemoteControl: ''{0}'' forbidden by user''s choice", myCommand);
161 throw new RequestHandlerForbiddenException(err);
162 }
163 }
164 }
165
166 /**
167 * Set request URL and parse args.
168 *
169 * @param url The request URL.
170 */
171 public void setUrl(String url) {
172 this.request = url;
173 parseArgs();
174 }
175
176 /**
177 * Parse the request parameters as key=value pairs.
178 * The result will be stored in {@code this.args}.
179 *
180 * Can be overridden by subclass.
181 */
182 protected void parseArgs() {
183 try {
184 String req = URLDecoder.decode(this.request, "UTF-8");
185 HashMap<String, String> args = new HashMap<String, String>();
186 if (req.indexOf('?') != -1) {
187 String query = req.substring(req.indexOf('?') + 1);
188 if (query.indexOf('#') != -1) {
189 query = query.substring(0, query.indexOf('#'));
190 }
191 String[] params = query.split("&", -1);
192 for (String param : params) {
193 int eq = param.indexOf('=');
194 if (eq != -1) {
195 args.put(param.substring(0, eq), param.substring(eq + 1));
196 }
197 }
198 }
199 this.args = args;
200 } catch (UnsupportedEncodingException ex) {
201 throw new IllegalStateException(ex);
202 }
203 }
204
205 void checkMandatoryParams() throws RequestHandlerBadRequestException {
206 String[] mandatory = getMandatoryParams();
207 String[] optional = getOptionalParams();
208 List<String> missingKeys = new LinkedList<String>();
209 boolean error = false;
210 if(mandatory != null) for (String key : mandatory) {
211 String value = args.get(key);
212 if ((value == null) || (value.length() == 0)) {
213 error = true;
214 Main.warn("'" + myCommand + "' remote control request must have '" + key + "' parameter");
215 missingKeys.add(key);
216 }
217 }
218 HashSet<String> knownParams = new HashSet<String>();
219 if (mandatory != null) Collections.addAll(knownParams, mandatory);
220 if (optional != null) Collections.addAll(knownParams, optional);
221 for (String par: args.keySet()) {
222 if (!knownParams.contains(par)) {
223 Main.warn("Unknown remote control parameter {0}, skipping it", par);
224 }
225 }
226 if (error) {
227 throw new RequestHandlerBadRequestException(
228 "The following keys are mandatory, but have not been provided: "
229 + Utils.join(", ", missingKeys));
230 }
231
232 }
233
234 /**
235 * Save command associated with this handler.
236 *
237 * @param command The command.
238 */
239 public void setCommand(String command)
240 {
241 if (command.charAt(0) == '/') {
242 command = command.substring(1);
243 }
244 myCommand = command;
245 }
246
247 public String getContent() {
248 return content;
249 }
250
251 public String getContentType() {
252 return contentType;
253 }
254
255 protected boolean isLoadInNewLayer() {
256 return args.get("new_layer") != null && !args.get("new_layer").isEmpty()
257 ? Boolean.parseBoolean(args.get("new_layer"))
258 : Main.pref.getBoolean(loadInNewLayerKey, loadInNewLayerDefault);
259 }
260
261 protected final String decodeParam(String param) {
262 try {
263 return URLDecoder.decode(param, "UTF-8");
264 } catch (UnsupportedEncodingException e) {
265 throw new RuntimeException();
266 }
267 }
268
269 public void setSender(String sender) {
270 this.sender = sender;
271 }
272
273 public static class RequestHandlerException extends Exception {
274
275 public RequestHandlerException(String message) {
276 super(message);
277 }
278
279 public RequestHandlerException() {
280 }
281 }
282
283 public static class RequestHandlerErrorException extends RequestHandlerException {
284 }
285
286 public static class RequestHandlerBadRequestException extends RequestHandlerException {
287
288 public RequestHandlerBadRequestException(String message) {
289 super(message);
290 }
291 }
292
293 public static class RequestHandlerForbiddenException extends RequestHandlerException {
294 private static final long serialVersionUID = 2263904699747115423L;
295
296 public RequestHandlerForbiddenException(String message) {
297 super(message);
298 }
299 }
300}
Note: See TracBrowser for help on using the repository browser.