Commit 3fb6dc861087ed3d2ded6c6e14660fc6fd5b773f
1 parent
81beb05d
Add WebPrefs framework, detail: https://github.com/Mumfrey/webprefs
Showing
33 changed files
with
2734 additions
and
0 deletions
src/client/java/com/mumfrey/webprefs/WebPreferences.java
0 → 100644
1 | +package com.mumfrey.webprefs; | |
2 | + | |
3 | +import java.util.HashMap; | |
4 | +import java.util.HashSet; | |
5 | +import java.util.Map; | |
6 | +import java.util.Set; | |
7 | +import java.util.concurrent.ConcurrentHashMap; | |
8 | +import java.util.regex.Pattern; | |
9 | + | |
10 | +import com.mumfrey.liteloader.util.log.LiteLoaderLogger; | |
11 | +import com.mumfrey.webprefs.exceptions.InvalidKeyException; | |
12 | +import com.mumfrey.webprefs.exceptions.InvalidValueException; | |
13 | +import com.mumfrey.webprefs.exceptions.ReadOnlyPreferencesException; | |
14 | +import com.mumfrey.webprefs.framework.RequestFailureReason; | |
15 | +import com.mumfrey.webprefs.interfaces.IWebPreferences; | |
16 | +import com.mumfrey.webprefs.interfaces.IWebPreferencesClient; | |
17 | +import com.mumfrey.webprefs.interfaces.IWebPreferencesProvider; | |
18 | + | |
19 | +class WebPreferences implements IWebPreferences | |
20 | +{ | |
21 | + /** | |
22 | + * The update frequency to use when operating normally, this is the | |
23 | + * frequency that new requests will be submitted to the remote request queue | |
24 | + */ | |
25 | + private static final int UPDATE_FREQUENCY_TICKS = 20; // 1 second | |
26 | + | |
27 | + /** | |
28 | + * Number of ticks to wait before a request is assumed to have timed out | |
29 | + */ | |
30 | + private static final int REQUEST_TIMEOUT_TICKS = 20 * 60; // 1 minute | |
31 | + | |
32 | + /** | |
33 | + * Number of ticks to wait on any communication error (request failed at the | |
34 | + * server, request failed to be submitted, request timed out, etc.) | |
35 | + */ | |
36 | + private static final int UPDATE_ERROR_SUSPEND_TICKS = 20 * 60; // 1 minute | |
37 | + | |
38 | + /** | |
39 | + * Pattern for validating keys | |
40 | + */ | |
41 | + private static final Pattern keyPattern = Pattern.compile("^[a-z0-9_\\-\\.]{1,32}$"); | |
42 | + | |
43 | + /** | |
44 | + * Preferences client, this is a delegate which is used to communicate with | |
45 | + * the preferences provider | |
46 | + * | |
47 | + * @author Adam Mummery-Smith | |
48 | + */ | |
49 | + class Client implements IWebPreferencesClient | |
50 | + { | |
51 | + @Override | |
52 | + public void onGetRequestSuccess(String uuid, Map<String, String> values) | |
53 | + { | |
54 | + if (!WebPreferences.this.uuid.equals(uuid)) | |
55 | + { | |
56 | + throw new RuntimeException("Received unsolicited response"); | |
57 | + } | |
58 | + | |
59 | + WebPreferences.this.onGetRequestSuccess(values); | |
60 | + } | |
61 | + | |
62 | + @Override | |
63 | + public void onSetRequestSuccess(String uuid, Set<String> keys) | |
64 | + { | |
65 | + if (!WebPreferences.this.uuid.equals(uuid)) | |
66 | + { | |
67 | + throw new RuntimeException("Received unsolicited response"); | |
68 | + } | |
69 | + | |
70 | + WebPreferences.this.onSetRequestSuccess(keys); | |
71 | + } | |
72 | + | |
73 | + @Override | |
74 | + public void onGetRequestFailed(String uuid, Set<String> keys, RequestFailureReason reason) | |
75 | + { | |
76 | + if (!WebPreferences.this.uuid.equals(uuid)) | |
77 | + { | |
78 | + throw new RuntimeException("Received unsolicited response"); | |
79 | + } | |
80 | + | |
81 | + WebPreferences.this.onGetRequestFailed(keys, reason); | |
82 | + } | |
83 | + | |
84 | + @Override | |
85 | + public void onSetRequestFailed(String uuid, Set<String> keys, RequestFailureReason reason) | |
86 | + { | |
87 | + if (!WebPreferences.this.uuid.equals(uuid)) | |
88 | + { | |
89 | + throw new RuntimeException("Received unsolicited response"); | |
90 | + } | |
91 | + | |
92 | + WebPreferences.this.onSetRequestFailed(keys, reason); | |
93 | + } | |
94 | + } | |
95 | + | |
96 | + /** | |
97 | + * Preferences provider | |
98 | + */ | |
99 | + private final IWebPreferencesProvider provider; | |
100 | + | |
101 | + /** | |
102 | + * Preferences delegate | |
103 | + */ | |
104 | + private final IWebPreferencesClient client; | |
105 | + | |
106 | + /** | |
107 | + * Our UUID | |
108 | + */ | |
109 | + protected final String uuid; | |
110 | + | |
111 | + /** | |
112 | + * True if we are a private settings set | |
113 | + */ | |
114 | + protected final boolean isPrivate; | |
115 | + | |
116 | + protected final boolean isReadOnly; | |
117 | + | |
118 | + /** | |
119 | + * Current key/value pairs | |
120 | + */ | |
121 | + protected final Map<String, String> prefs = new ConcurrentHashMap<String, String>(); | |
122 | + | |
123 | + /** | |
124 | + * Keys which have been requested by a consumer but not requested from the | |
125 | + * server yet | |
126 | + */ | |
127 | + protected final Set<String> requestedPrefs = new HashSet<String>(); | |
128 | + | |
129 | + /** | |
130 | + * Keys which have been requested from the server but not received yet | |
131 | + */ | |
132 | + protected final Set<String> pendingPrefs = new HashSet<String>(); | |
133 | + | |
134 | + /** | |
135 | + * Keys which have been set by a consumer but not sent to the server yet | |
136 | + */ | |
137 | + protected final Set<String> dirtyPrefs = new HashSet<String>(); | |
138 | + | |
139 | + /** | |
140 | + * Concurrency lock | |
141 | + */ | |
142 | + protected final Object lock = new Object(); | |
143 | + | |
144 | + /** | |
145 | + * True when any kind of | |
146 | + */ | |
147 | + protected volatile boolean dirty = false; | |
148 | + | |
149 | + private volatile int updateCheckTimer = 1; | |
150 | + | |
151 | + protected int requestTimeoutTimer = 0; | |
152 | + | |
153 | + WebPreferences(IWebPreferencesProvider provider, String uuid, boolean isPrivate, boolean isReadOnly) | |
154 | + { | |
155 | + this.provider = provider; | |
156 | + this.uuid = uuid; | |
157 | + this.isPrivate = isPrivate; | |
158 | + this.isReadOnly = isReadOnly; | |
159 | + this.client = new Client(); | |
160 | + } | |
161 | + | |
162 | + /* (non-Javadoc) | |
163 | + * @see com.mumfrey.webprefs.interfaces.IWebPreferences#getUUID() | |
164 | + */ | |
165 | + @Override | |
166 | + public String getUUID() | |
167 | + { | |
168 | + return this.uuid; | |
169 | + } | |
170 | + | |
171 | + /* (non-Javadoc) | |
172 | + * @see com.mumfrey.webprefs.interfaces.IWebPreferences#isPrivate() | |
173 | + */ | |
174 | + @Override | |
175 | + public boolean isPrivate() | |
176 | + { | |
177 | + return this.isPrivate; | |
178 | + } | |
179 | + | |
180 | + /* (non-Javadoc) | |
181 | + * @see com.mumfrey.webprefs.interfaces.IWebPreferences#isReadOnly() | |
182 | + */ | |
183 | + @Override | |
184 | + public boolean isReadOnly() | |
185 | + { | |
186 | + return this.isReadOnly; | |
187 | + } | |
188 | + | |
189 | + void onTick() | |
190 | + { | |
191 | + if (this.updateCheckTimer > 0) | |
192 | + { | |
193 | + this.updateCheckTimer--; | |
194 | + if (this.updateCheckTimer < 1) | |
195 | + { | |
196 | + this.update(); | |
197 | + } | |
198 | + } | |
199 | + | |
200 | + if (this.requestTimeoutTimer > 0) | |
201 | + { | |
202 | + this.requestTimeoutTimer--; | |
203 | + if (this.requestTimeoutTimer < 1) | |
204 | + { | |
205 | + this.handleTimeout(); | |
206 | + } | |
207 | + } | |
208 | + } | |
209 | + | |
210 | + /** | |
211 | + * Handle server requests on a periodic basis | |
212 | + */ | |
213 | + private void update() | |
214 | + { | |
215 | + this.updateCheckTimer = WebPreferences.UPDATE_FREQUENCY_TICKS; | |
216 | + | |
217 | + if (!this.dirty || !this.provider.isActive()) | |
218 | + { | |
219 | + return; | |
220 | + } | |
221 | + | |
222 | + synchronized (this.lock) | |
223 | + { | |
224 | + this.dirty = false; | |
225 | + | |
226 | + if (this.requestedPrefs.size() > 0) | |
227 | + { | |
228 | + LiteLoaderLogger.debug("Preferences for " + this.uuid + " is submitting a request for " | |
229 | + + this.requestedPrefs.size() + " requested preferences"); | |
230 | + if (this.provider.requestGet(this.client, this.uuid, new HashSet<String>(this.requestedPrefs), this.isPrivate)) | |
231 | + { | |
232 | + this.requestTimeoutTimer = WebPreferences.REQUEST_TIMEOUT_TICKS; | |
233 | + this.pendingPrefs.addAll(this.requestedPrefs); | |
234 | + this.requestedPrefs.clear(); | |
235 | + } | |
236 | + else | |
237 | + { | |
238 | + this.dirty = true; | |
239 | + } | |
240 | + } | |
241 | + | |
242 | + } | |
243 | + | |
244 | + this.commit(false); | |
245 | + } | |
246 | + | |
247 | + /** | |
248 | + * Called when a pending request is deemed to have timed out | |
249 | + */ | |
250 | + private void handleTimeout() | |
251 | + { | |
252 | + this.updateCheckTimer = WebPreferences.UPDATE_ERROR_SUSPEND_TICKS; | |
253 | + | |
254 | + synchronized (this.lock) | |
255 | + { | |
256 | + this.requestedPrefs.addAll(this.pendingPrefs); | |
257 | + this.pendingPrefs.clear(); | |
258 | + this.dirty = true; | |
259 | + } | |
260 | + } | |
261 | + | |
262 | + /* (non-Javadoc) | |
263 | + * @see com.mumfrey.webprefs.interfaces.IWebPreferences | |
264 | + * #request(java.lang.String) | |
265 | + */ | |
266 | + @Override | |
267 | + public void request(String key) | |
268 | + { | |
269 | + WebPreferences.validateKey(key); | |
270 | + | |
271 | + synchronized (this.lock) | |
272 | + { | |
273 | + this.dirty |= this.addRequestedKey(key); | |
274 | + } | |
275 | + } | |
276 | + | |
277 | + /* (non-Javadoc) | |
278 | + * @see com.mumfrey.webprefs.interfaces.IWebPreferences | |
279 | + * #request(java.lang.String[]) | |
280 | + */ | |
281 | + @Override | |
282 | + public void request(String... keys) | |
283 | + { | |
284 | + if (keys.length < 1) return; | |
285 | + if (keys.length == 1) this.request(keys[0]); | |
286 | + | |
287 | + synchronized (this.lock) | |
288 | + { | |
289 | + boolean dirty = false; | |
290 | + | |
291 | + for (String key : keys) | |
292 | + { | |
293 | + WebPreferences.validateKey(key); | |
294 | + dirty |= this.addRequestedKey(key); | |
295 | + } | |
296 | + | |
297 | + this.dirty |= dirty; | |
298 | + } | |
299 | + } | |
300 | + | |
301 | + /* (non-Javadoc) | |
302 | + * @see com.mumfrey.webprefs.interfaces.IWebPreferences | |
303 | + * #request(java.util.Set) | |
304 | + */ | |
305 | + @Override | |
306 | + public void request(Set<String> keys) | |
307 | + { | |
308 | + if (keys == null || keys.size() < 1) return; | |
309 | + | |
310 | + synchronized (this.lock) | |
311 | + { | |
312 | + boolean dirty = false; | |
313 | + | |
314 | + for (String key : keys) | |
315 | + { | |
316 | + WebPreferences.validateKey(key); | |
317 | + dirty |= this.addRequestedKey(key); | |
318 | + } | |
319 | + | |
320 | + this.dirty |= dirty; | |
321 | + } | |
322 | + } | |
323 | + | |
324 | + /* (non-Javadoc) | |
325 | + * @see com.mumfrey.webprefs.interfaces.IWebPreferences#poll() | |
326 | + */ | |
327 | + @Override | |
328 | + public void poll() | |
329 | + { | |
330 | + synchronized (this.lock) | |
331 | + { | |
332 | + this.requestedPrefs.addAll(this.prefs.keySet()); | |
333 | + this.requestedPrefs.removeAll(this.pendingPrefs); | |
334 | + this.dirty = true; | |
335 | + } | |
336 | + } | |
337 | + | |
338 | + /* (non-Javadoc) | |
339 | + * @see com.mumfrey.webprefs.interfaces.IWebPreferences#commit(boolean) | |
340 | + */ | |
341 | + @Override | |
342 | + public void commit(boolean force) | |
343 | + { | |
344 | + synchronized (this.lock) | |
345 | + { | |
346 | + // Permanent error condition | |
347 | + if (this.updateCheckTimer < 0) | |
348 | + { | |
349 | + return; | |
350 | + } | |
351 | + | |
352 | + if (force) | |
353 | + { | |
354 | + this.dirtyPrefs.addAll(this.prefs.keySet()); | |
355 | + } | |
356 | + | |
357 | + if (this.dirtyPrefs.size() > 0) | |
358 | + { | |
359 | + Map<String, String> outgoingPrefs = new HashMap<String, String>(); | |
360 | + for (String key : this.dirtyPrefs) | |
361 | + { | |
362 | + outgoingPrefs.put(key, this.prefs.get(key)); | |
363 | + } | |
364 | + | |
365 | + LiteLoaderLogger.debug("Preferences for " + this.uuid + " is submitting a SET for " + outgoingPrefs.size() + " dirty preferences"); | |
366 | + if (this.provider.requestSet(this.client, this.uuid, outgoingPrefs, this.isPrivate)) | |
367 | + { | |
368 | + this.dirtyPrefs.clear(); | |
369 | + } | |
370 | + else | |
371 | + { | |
372 | + this.dirty = true; | |
373 | + } | |
374 | + } | |
375 | + } | |
376 | + } | |
377 | + | |
378 | + /* (non-Javadoc) | |
379 | + * @see com.mumfrey.webprefs.interfaces.IWebPreferences | |
380 | + * #has(java.lang.String) | |
381 | + */ | |
382 | + @Override | |
383 | + public boolean has(String key) | |
384 | + { | |
385 | + WebPreferences.validateKey(key); | |
386 | + | |
387 | + return this.get(key) != null; | |
388 | + } | |
389 | + | |
390 | + /* (non-Javadoc) | |
391 | + * @see com.mumfrey.webprefs.interfaces.IWebPreferences | |
392 | + * #get(java.lang.String) | |
393 | + */ | |
394 | + @Override | |
395 | + public String get(String key) | |
396 | + { | |
397 | + WebPreferences.validateKey(key); | |
398 | + | |
399 | + // .get() can be outside of the synchronisation lock because we are using ConcurrentHashSet | |
400 | + String value = this.prefs.get(key); | |
401 | + | |
402 | + if (value == null) | |
403 | + { | |
404 | + synchronized (this.lock) | |
405 | + { | |
406 | + this.dirty |= this.addRequestedKey(key); | |
407 | + } | |
408 | + } | |
409 | + | |
410 | + return value; | |
411 | + } | |
412 | + | |
413 | + /* (non-Javadoc) | |
414 | + * @see com.mumfrey.webprefs.interfaces.IWebPreferences | |
415 | + * #get(java.lang.String, java.lang.String) | |
416 | + */ | |
417 | + @Override | |
418 | + public String get(String key, String defaultValue) | |
419 | + { | |
420 | + WebPreferences.validateKey(key); | |
421 | + | |
422 | + String value = this.get(key); | |
423 | + return value != null ? value : defaultValue; | |
424 | + } | |
425 | + | |
426 | + /* (non-Javadoc) | |
427 | + * @see com.mumfrey.webprefs.interfaces.IWebPreferences | |
428 | + * #set(java.lang.String, java.lang.String) | |
429 | + */ | |
430 | + @Override | |
431 | + public void set(String key, String value) | |
432 | + { | |
433 | + if (this.isReadOnly()) | |
434 | + { | |
435 | + throw new ReadOnlyPreferencesException("Preference collection for " + this.uuid + " is read-only"); | |
436 | + } | |
437 | + | |
438 | + WebPreferences.validateKV(key, value); | |
439 | + | |
440 | + synchronized (this.lock) | |
441 | + { | |
442 | + String oldValue = this.prefs.get(key); | |
443 | + if (value.equals(oldValue)) return; | |
444 | + | |
445 | + this.prefs.put(key, value); | |
446 | + this.dirtyPrefs.add(key); | |
447 | + this.requestedPrefs.remove(key); | |
448 | + this.dirty = true; | |
449 | + } | |
450 | + } | |
451 | + | |
452 | + /* (non-Javadoc) | |
453 | + * @see com.mumfrey.webprefs.interfaces.IWebPreferences | |
454 | + * #remove(java.lang.String) | |
455 | + */ | |
456 | + @Override | |
457 | + public void remove(String key) | |
458 | + { | |
459 | + this.set(key, ""); | |
460 | + } | |
461 | + | |
462 | + /** | |
463 | + * Add a key to the current request set, the key will be requested from the | |
464 | + * server on the next {@link #update()} | |
465 | + * | |
466 | + * @param key | |
467 | + * @return | |
468 | + */ | |
469 | + private boolean addRequestedKey(String key) | |
470 | + { | |
471 | + if (key != null && !this.pendingPrefs.contains(key)) | |
472 | + { | |
473 | + this.requestedPrefs.add(key); | |
474 | + return true; | |
475 | + } | |
476 | + | |
477 | + return false; | |
478 | + } | |
479 | + | |
480 | + /** | |
481 | + * Callback from the preferences provider | |
482 | + */ | |
483 | + void onGetRequestSuccess(Map<String, String> values) | |
484 | + { | |
485 | + this.requestTimeoutTimer = 0; | |
486 | + | |
487 | + synchronized (this.lock) | |
488 | + { | |
489 | + this.prefs.putAll(values); | |
490 | + | |
491 | + Set<String> keys = values.keySet(); | |
492 | + this.dirtyPrefs.removeAll(keys); | |
493 | + this.pendingPrefs.removeAll(keys); | |
494 | + this.requestedPrefs.removeAll(keys); | |
495 | + } | |
496 | + } | |
497 | + | |
498 | + /** | |
499 | + * Callback from the preferences provider | |
500 | + */ | |
501 | + void onSetRequestSuccess(Set<String> keys) | |
502 | + { | |
503 | + this.requestTimeoutTimer = 0; | |
504 | + | |
505 | + synchronized (this.lock) | |
506 | + { | |
507 | + this.dirtyPrefs.removeAll(keys); | |
508 | + this.requestedPrefs.removeAll(keys); | |
509 | + this.dirty = (this.dirtyPrefs.size() > 0 || this.requestedPrefs.size() > 0); | |
510 | + } | |
511 | + } | |
512 | + | |
513 | + /** | |
514 | + * Callback from the preferences provider | |
515 | + * @param reason | |
516 | + */ | |
517 | + void onGetRequestFailed(Set<String> keys, RequestFailureReason reason) | |
518 | + { | |
519 | + this.requestTimeoutTimer = 0; | |
520 | + this.handleFailedRequest(reason); | |
521 | + | |
522 | + synchronized (this.lock) | |
523 | + { | |
524 | + this.dirtyPrefs.addAll(keys); | |
525 | + this.pendingPrefs.removeAll(keys); | |
526 | + this.dirty = true; | |
527 | + } | |
528 | + } | |
529 | + | |
530 | + /** | |
531 | + * Callback from the preferences provider | |
532 | + * @param reason | |
533 | + */ | |
534 | + void onSetRequestFailed(Set<String> keys, RequestFailureReason reason) | |
535 | + { | |
536 | + this.requestTimeoutTimer = 0; | |
537 | + this.handleFailedRequest(reason); | |
538 | + | |
539 | + synchronized (this.lock) | |
540 | + { | |
541 | + this.requestedPrefs.addAll(keys); | |
542 | + this.pendingPrefs.removeAll(keys); | |
543 | + this.dirty = true; | |
544 | + } | |
545 | + } | |
546 | + | |
547 | + /** | |
548 | + * @param reason | |
549 | + */ | |
550 | + private void handleFailedRequest(RequestFailureReason reason) | |
551 | + { | |
552 | + if (reason.isPermanent()) | |
553 | + { | |
554 | + LiteLoaderLogger.debug("Halting update of preferences for " + this.uuid + " permanently because " + reason); | |
555 | + this.updateCheckTimer = -1; | |
556 | + } | |
557 | + | |
558 | + int suspendUpdateFor = WebPreferences.UPDATE_ERROR_SUSPEND_TICKS * Math.max(1, reason.getSeverity()); | |
559 | + LiteLoaderLogger.debug("Suspending update of preferences for " + this.uuid + " for " + suspendUpdateFor + " because " + reason); | |
560 | + this.updateCheckTimer = suspendUpdateFor; | |
561 | + } | |
562 | + | |
563 | + /** | |
564 | + * @param key | |
565 | + */ | |
566 | + protected static final void validateKey(String key) | |
567 | + { | |
568 | + if (key == null || !WebPreferences.keyPattern.matcher(key).matches()) | |
569 | + { | |
570 | + throw new InvalidKeyException("The specified key [" + key + "] is not valid"); | |
571 | + } | |
572 | + } | |
573 | + | |
574 | + /** | |
575 | + * @param key | |
576 | + * @param value | |
577 | + */ | |
578 | + protected static final void validateKV(String key, String value) | |
579 | + { | |
580 | + WebPreferences.validateKey(key); | |
581 | + | |
582 | + if (value == null || value.length() > 255) | |
583 | + { | |
584 | + throw new InvalidValueException("The specified value [" + value + "] for key [" + key + "] is not valid"); | |
585 | + } | |
586 | + } | |
587 | +} | ... | ... |
src/client/java/com/mumfrey/webprefs/WebPreferencesManager.java
0 → 100644
1 | +package com.mumfrey.webprefs; | |
2 | + | |
3 | +import java.io.File; | |
4 | +import java.net.Proxy; | |
5 | +import java.net.URI; | |
6 | +import java.net.URISyntaxException; | |
7 | +import java.util.HashMap; | |
8 | +import java.util.LinkedHashMap; | |
9 | +import java.util.LinkedList; | |
10 | +import java.util.List; | |
11 | +import java.util.Map; | |
12 | +import java.util.UUID; | |
13 | +import java.util.regex.Matcher; | |
14 | +import java.util.regex.Pattern; | |
15 | + | |
16 | +import com.mojang.authlib.GameProfile; | |
17 | +import com.mojang.realmsclient.dto.RealmsServer; | |
18 | +import com.mumfrey.liteloader.JoinGameListener; | |
19 | +import com.mumfrey.liteloader.Tickable; | |
20 | +import com.mumfrey.liteloader.core.LiteLoader; | |
21 | +import com.mumfrey.webprefs.exceptions.InvalidServiceException; | |
22 | +import com.mumfrey.webprefs.exceptions.InvalidUUIDException; | |
23 | +import com.mumfrey.webprefs.framework.WebPreferencesProvider; | |
24 | +import com.mumfrey.webprefs.interfaces.IWebPreferences; | |
25 | + | |
26 | +import net.minecraft.client.Minecraft; | |
27 | +import net.minecraft.client.multiplayer.ServerData; | |
28 | +import net.minecraft.entity.player.EntityPlayer; | |
29 | +import net.minecraft.network.INetHandler; | |
30 | +import net.minecraft.network.play.server.SPacketJoinGame; | |
31 | +import net.minecraft.util.Session; | |
32 | + | |
33 | +/** | |
34 | + * WebPreferences Service Manager, acts as a central registry for all web | |
35 | + * preference collections. | |
36 | + * | |
37 | + * <p>To access preferences on a service, first request the service using | |
38 | + * {@link #get(String)} or {@link #getDefault()}, you can then request | |
39 | + * preferences objects from the service by calling the various overloads of | |
40 | + * {@link #getPreferences}.</p> | |
41 | + * | |
42 | + * @author Adam Mummery-Smith | |
43 | + */ | |
44 | +public final class WebPreferencesManager | |
45 | +{ | |
46 | + /** | |
47 | + * WebPreferences Manager Update Daemon is injected into LiteLoader to | |
48 | + * facilitate passing events to the WebPreferences Manager without having to | |
49 | + * expose public callback methods. | |
50 | + * | |
51 | + * @author Adam Mummery-Smith | |
52 | + */ | |
53 | + static class WebPreferencesUpdateDeamon implements Tickable, JoinGameListener | |
54 | + { | |
55 | + @Override | |
56 | + public String getName() | |
57 | + { | |
58 | + return "Web Preferences Update Daemon"; | |
59 | + } | |
60 | + | |
61 | + @Override | |
62 | + public String getVersion() | |
63 | + { | |
64 | + return "N/A"; | |
65 | + } | |
66 | + | |
67 | + @Override | |
68 | + public void init(File configPath) | |
69 | + { | |
70 | + } | |
71 | + | |
72 | + @Override | |
73 | + public void upgradeSettings(String version, File configPath, File oldConfigPath) | |
74 | + { | |
75 | + } | |
76 | + | |
77 | + @Override | |
78 | + public void onTick(Minecraft minecraft, float partialTicks, boolean inGame, boolean clock) | |
79 | + { | |
80 | + if (clock) | |
81 | + { | |
82 | + for (WebPreferencesManager manager : WebPreferencesManager.managers.values()) | |
83 | + { | |
84 | + manager.onTick(); | |
85 | + } | |
86 | + } | |
87 | + } | |
88 | + | |
89 | + @Override | |
90 | + public void onJoinGame(INetHandler netHandler, SPacketJoinGame joinGamePacket, ServerData serverData, RealmsServer realmsServer) | |
91 | + { | |
92 | + for (WebPreferencesManager manager : WebPreferencesManager.managers.values()) | |
93 | + { | |
94 | + manager.onJoinGame(); | |
95 | + } | |
96 | + } | |
97 | + } | |
98 | + | |
99 | + /** | |
100 | + * Default KV api hostname to connect to | |
101 | + */ | |
102 | + private static final String DEFAULT_HOSTNAME = "kv.liteloader.com"; | |
103 | + | |
104 | + /** | |
105 | + * Regex for validating UUIDs | |
106 | + */ | |
107 | + private static final Pattern uuidPattern = Pattern.compile("^[a-f0-9]{32}$"); | |
108 | + | |
109 | + /** | |
110 | + * Mapping of hostnames to managers | |
111 | + */ | |
112 | + static final Map<String, WebPreferencesManager> managers = new LinkedHashMap<String, WebPreferencesManager>(); | |
113 | + | |
114 | + /** | |
115 | + * Update daemon | |
116 | + */ | |
117 | + private static WebPreferencesUpdateDeamon updateDeamon; | |
118 | + | |
119 | + /** | |
120 | + * Session for this instance | |
121 | + */ | |
122 | + private final Session session; | |
123 | + | |
124 | + /** | |
125 | + * Preference provider, manages queueing requests and passing responses back | |
126 | + * to clients | |
127 | + */ | |
128 | + private final WebPreferencesProvider provider; | |
129 | + | |
130 | + /** | |
131 | + * All preference sets, for iteration purposes | |
132 | + */ | |
133 | + private final List<WebPreferences> allPreferences = new LinkedList<WebPreferences>(); | |
134 | + | |
135 | + /** | |
136 | + * All public preference sets, mapped by UUID | |
137 | + */ | |
138 | + private final Map<String, IWebPreferences> preferencesPublic = new HashMap<String, IWebPreferences>(); | |
139 | + | |
140 | + /** | |
141 | + * All private preference sets, mapped by UUID | |
142 | + */ | |
143 | + private final Map<String, IWebPreferences> preferencesPrivate = new HashMap<String, IWebPreferences>(); | |
144 | + | |
145 | + private WebPreferencesManager(Proxy proxy, Session session, String hostName) | |
146 | + { | |
147 | + this.session = session; | |
148 | + this.provider = new WebPreferencesProvider(proxy, session, hostName, 50); | |
149 | + } | |
150 | + | |
151 | + void onTick() | |
152 | + { | |
153 | + this.provider.onTick(); | |
154 | + | |
155 | + for (WebPreferences prefs : this.allPreferences) | |
156 | + { | |
157 | + try | |
158 | + { | |
159 | + prefs.onTick(); | |
160 | + } | |
161 | + catch (Exception ex) {} | |
162 | + } | |
163 | + } | |
164 | + | |
165 | + void onJoinGame() | |
166 | + { | |
167 | + for (WebPreferences prefs : this.allPreferences) | |
168 | + { | |
169 | + try | |
170 | + { | |
171 | + prefs.poll(); | |
172 | + } | |
173 | + catch (Exception ex) {} | |
174 | + } | |
175 | + } | |
176 | + | |
177 | + | |
178 | + /** | |
179 | + * Get a public or private preferences collection for the local player | |
180 | + * | |
181 | + * @param privatePrefs true to fetch the player's private preferences, false | |
182 | + * to fetch public preferences | |
183 | + * @return player's preference collection, creates if necessary | |
184 | + */ | |
185 | + public IWebPreferences getLocalPreferences(boolean privatePrefs) | |
186 | + { | |
187 | + return this.getPreferences(this.session.getPlayerID(), privatePrefs); | |
188 | + } | |
189 | + | |
190 | + /** | |
191 | + * Get a public preferences collection for the specified player. | |
192 | + * | |
193 | + * @param player Player to fetch preferences for | |
194 | + * @param privatePrefs True to fetch the player's private preferences, false | |
195 | + * to fetch the public preferences | |
196 | + * @return Preference collection or <tt>null</tt> if the player's profile | |
197 | + * cannot be retrieved | |
198 | + */ | |
199 | + public IWebPreferences getPreferences(EntityPlayer player) | |
200 | + { | |
201 | + return this.getPreferences(player, false); | |
202 | + } | |
203 | + | |
204 | + /** | |
205 | + * Get a public or private preferences collection for the specified player, | |
206 | + * note that accessing a private collection for another player is likely | |
207 | + * to be prohibited by the service. | |
208 | + * | |
209 | + * @param player Player to fetch preferences for | |
210 | + * @param privatePrefs True to fetch the player's private preferences, false | |
211 | + * to fetch the public preferences | |
212 | + * @return Preference collection or <tt>null</tt> if the player's profile | |
213 | + * cannot be retrieved | |
214 | + */ | |
215 | + public IWebPreferences getPreferences(EntityPlayer player, boolean privatePrefs) | |
216 | + { | |
217 | + GameProfile gameProfile = player.getGameProfile(); | |
218 | + return gameProfile != null ? this.getPreferences(gameProfile, privatePrefs) : null; | |
219 | + } | |
220 | + | |
221 | + /** | |
222 | + * Get a public preferences collection for the specified game profile. | |
223 | + * | |
224 | + * @param gameProfile game profile to fetch preferences for | |
225 | + * @return Preference collection or <tt>null</tt> if the supplied profile is | |
226 | + * null | |
227 | + */ | |
228 | + public IWebPreferences getPreferences(GameProfile gameProfile) | |
229 | + { | |
230 | + return gameProfile != null ? this.getPreferences(gameProfile, false) : null; | |
231 | + } | |
232 | + | |
233 | + /** | |
234 | + * Get a public or private preferences collection for the specified game | |
235 | + * profile, note that accessing a private collection for another player is | |
236 | + * likely to be prohibited by the service. | |
237 | + * | |
238 | + * @param gameProfile game profile to fetch preferences for | |
239 | + * @param privatePrefs True to fetch the player's private preferences, false | |
240 | + * to fetch the public preferences | |
241 | + * @return Preference collection or <tt>null</tt> if the supplied profile is | |
242 | + * null | |
243 | + */ | |
244 | + public IWebPreferences getPreferences(GameProfile gameProfile, boolean privatePrefs) | |
245 | + { | |
246 | + return gameProfile != null ? this.getPreferences(gameProfile.getId(), privatePrefs) : null; | |
247 | + } | |
248 | + | |
249 | + /** | |
250 | + * Get a public preferences collection for the specified player UUID. | |
251 | + * | |
252 | + * @param uuid UUID to fetch preferences for | |
253 | + * @return Preference collection or <tt>null</tt> if the supplied UUID is | |
254 | + * null | |
255 | + */ | |
256 | + public IWebPreferences getPreferences(UUID uuid) | |
257 | + { | |
258 | + return uuid != null ? this.getPreferences(uuid, false) : null; | |
259 | + } | |
260 | + | |
261 | + /** | |
262 | + * Get a public or private preferences collection for the specified player | |
263 | + * UUID, note that accessing a private collection for another player is | |
264 | + * likely to be prohibited by the service. | |
265 | + * | |
266 | + * @param uuid UUID to fetch preferences for | |
267 | + * @param privatePrefs True to fetch the player's private preferences, false | |
268 | + * to fetch the public preferences | |
269 | + * @return Preference collection or <tt>null</tt> if the supplied UUID is | |
270 | + * null | |
271 | + */ | |
272 | + public IWebPreferences getPreferences(UUID uuid, boolean privatePrefs) | |
273 | + { | |
274 | + return uuid != null ? this.getPreferences(uuid.toString(), privatePrefs) : null; | |
275 | + } | |
276 | + | |
277 | + public IWebPreferences getPreferences(String uuid, boolean privatePrefs) | |
278 | + { | |
279 | + uuid = this.sanitiseUUID(uuid); | |
280 | + | |
281 | + Map<String, IWebPreferences> preferences = privatePrefs ? this.preferencesPrivate : this.preferencesPublic; | |
282 | + | |
283 | + IWebPreferences prefs = preferences.get(uuid); | |
284 | + | |
285 | + if (prefs == null) | |
286 | + { | |
287 | + WebPreferences newPrefs = new WebPreferences(this.provider, uuid, privatePrefs, !uuid.equals(this.session.getPlayerID())); | |
288 | + this.allPreferences.add(newPrefs); | |
289 | + preferences.put(uuid, newPrefs); | |
290 | + prefs = newPrefs; | |
291 | + } | |
292 | + | |
293 | + return prefs; | |
294 | + } | |
295 | + | |
296 | + private String sanitiseUUID(String uuid) | |
297 | + { | |
298 | + if (uuid == null) | |
299 | + { | |
300 | + throw new InvalidUUIDException("The UUID was null"); | |
301 | + } | |
302 | + | |
303 | + uuid = uuid.toLowerCase().replace("-", "").trim(); | |
304 | + Matcher uuidPatternMatcher = WebPreferencesManager.uuidPattern.matcher(uuid); | |
305 | + if (!uuidPatternMatcher.matches()) | |
306 | + { | |
307 | + throw new InvalidUUIDException("The specified string [" + uuid + "] is not a valid UUID"); | |
308 | + } | |
309 | + | |
310 | + return uuid; | |
311 | + } | |
312 | + | |
313 | + /** | |
314 | + * Get the default preferences manager (kv.liteloader.com) | |
315 | + * | |
316 | + * @return default preferences manager | |
317 | + */ | |
318 | + public static WebPreferencesManager getDefault() | |
319 | + { | |
320 | + return WebPreferencesManager.get(WebPreferencesManager.DEFAULT_HOSTNAME); | |
321 | + } | |
322 | + | |
323 | + /** | |
324 | + * Get a preferences manager for the specified service hostname | |
325 | + * | |
326 | + * @param hostName service hostname (bare hostname only, no protocol) | |
327 | + * @return preferences manager | |
328 | + * @throws InvalidServiceException if the specified host name is invalid | |
329 | + */ | |
330 | + @SuppressWarnings("unused") | |
331 | + public static WebPreferencesManager get(String hostName) throws InvalidServiceException | |
332 | + { | |
333 | + try | |
334 | + { | |
335 | + new URI(String.format("http://%s/", hostName)); | |
336 | + } | |
337 | + catch (URISyntaxException ex) | |
338 | + { | |
339 | + throw new InvalidServiceException("The specified service host was not valid: " + hostName, ex); | |
340 | + } | |
341 | + | |
342 | + if (WebPreferencesManager.updateDeamon == null) | |
343 | + { | |
344 | + WebPreferencesManager.updateDeamon = new WebPreferencesUpdateDeamon(); | |
345 | + LiteLoader.getInterfaceManager().registerListener(WebPreferencesManager.updateDeamon); | |
346 | + } | |
347 | + | |
348 | + WebPreferencesManager manager = WebPreferencesManager.managers.get(hostName); | |
349 | + | |
350 | + if (manager == null) | |
351 | + { | |
352 | + Minecraft minecraft = Minecraft.getMinecraft(); | |
353 | + | |
354 | + Proxy proxy = minecraft.getProxy(); | |
355 | + Session session = minecraft.getSession(); | |
356 | + | |
357 | + manager = new WebPreferencesManager(proxy, session, hostName); | |
358 | + WebPreferencesManager.managers.put(hostName, manager); | |
359 | + } | |
360 | + | |
361 | + return manager; | |
362 | + } | |
363 | +} | ... | ... |
src/client/java/com/mumfrey/webprefs/exceptions/InvalidKeyException.java
0 → 100644
1 | +package com.mumfrey.webprefs.exceptions; | |
2 | + | |
3 | +public class InvalidKeyException extends RuntimeException | |
4 | +{ | |
5 | + private static final long serialVersionUID = 1L; | |
6 | + | |
7 | + public InvalidKeyException() | |
8 | + { | |
9 | + } | |
10 | + | |
11 | + public InvalidKeyException(String message) | |
12 | + { | |
13 | + super(message); | |
14 | + } | |
15 | + | |
16 | + public InvalidKeyException(Throwable cause) | |
17 | + { | |
18 | + super(cause); | |
19 | + } | |
20 | + | |
21 | + public InvalidKeyException(String message, Throwable cause) | |
22 | + { | |
23 | + super(message, cause); | |
24 | + } | |
25 | +} | ... | ... |
src/client/java/com/mumfrey/webprefs/exceptions/InvalidPreferenceOperationException.java
0 → 100644
1 | +package com.mumfrey.webprefs.exceptions; | |
2 | + | |
3 | +public class InvalidPreferenceOperationException extends RuntimeException | |
4 | +{ | |
5 | + private static final long serialVersionUID = 1L; | |
6 | + | |
7 | + public InvalidPreferenceOperationException() | |
8 | + { | |
9 | + } | |
10 | + | |
11 | + public InvalidPreferenceOperationException(String message) | |
12 | + { | |
13 | + super(message); | |
14 | + } | |
15 | + | |
16 | + public InvalidPreferenceOperationException(Throwable cause) | |
17 | + { | |
18 | + super(cause); | |
19 | + } | |
20 | + | |
21 | + public InvalidPreferenceOperationException(String message, Throwable cause) | |
22 | + { | |
23 | + super(message, cause); | |
24 | + } | |
25 | +} | ... | ... |
src/client/java/com/mumfrey/webprefs/exceptions/InvalidRequestException.java
0 → 100644
1 | +package com.mumfrey.webprefs.exceptions; | |
2 | + | |
3 | +import com.mumfrey.webprefs.framework.RequestFailureReason; | |
4 | + | |
5 | +public class InvalidRequestException extends RuntimeException | |
6 | +{ | |
7 | + private static final long serialVersionUID = 1L; | |
8 | + | |
9 | + private final RequestFailureReason reason; | |
10 | + | |
11 | + public InvalidRequestException(RequestFailureReason reason) | |
12 | + { | |
13 | + this.reason = reason; | |
14 | + } | |
15 | + | |
16 | + public InvalidRequestException(RequestFailureReason reason, String message) | |
17 | + { | |
18 | + super(message); | |
19 | + this.reason = reason; | |
20 | + } | |
21 | + | |
22 | + public InvalidRequestException(RequestFailureReason reason, Throwable cause) | |
23 | + { | |
24 | + super(cause); | |
25 | + this.reason = reason; | |
26 | + } | |
27 | + | |
28 | + public InvalidRequestException(RequestFailureReason reason, String message, Throwable cause) | |
29 | + { | |
30 | + super(message, cause); | |
31 | + this.reason = reason; | |
32 | + } | |
33 | + | |
34 | + public RequestFailureReason getReason() | |
35 | + { | |
36 | + return this.reason; | |
37 | + } | |
38 | +} | ... | ... |
src/client/java/com/mumfrey/webprefs/exceptions/InvalidRequestKeyException.java
0 → 100644
1 | +package com.mumfrey.webprefs.exceptions; | |
2 | + | |
3 | +import com.mumfrey.webprefs.framework.RequestFailureReason; | |
4 | + | |
5 | +public class InvalidRequestKeyException extends InvalidRequestException | |
6 | +{ | |
7 | + private static final long serialVersionUID = 1L; | |
8 | + | |
9 | + public InvalidRequestKeyException() | |
10 | + { | |
11 | + super(RequestFailureReason.BAD_PARAMS); | |
12 | + } | |
13 | + | |
14 | + public InvalidRequestKeyException(String message) | |
15 | + { | |
16 | + super(RequestFailureReason.BAD_PARAMS, message); | |
17 | + } | |
18 | + | |
19 | + public InvalidRequestKeyException(Throwable cause) | |
20 | + { | |
21 | + super(RequestFailureReason.BAD_PARAMS, cause); | |
22 | + } | |
23 | + | |
24 | + public InvalidRequestKeyException(String message, Throwable cause) | |
25 | + { | |
26 | + super(RequestFailureReason.BAD_PARAMS, message, cause); | |
27 | + } | |
28 | +} | ... | ... |
src/client/java/com/mumfrey/webprefs/exceptions/InvalidRequestValueException.java
0 → 100644
1 | +package com.mumfrey.webprefs.exceptions; | |
2 | + | |
3 | +import com.mumfrey.webprefs.framework.RequestFailureReason; | |
4 | + | |
5 | +public class InvalidRequestValueException extends InvalidRequestException | |
6 | +{ | |
7 | + private static final long serialVersionUID = 1L; | |
8 | + | |
9 | + public InvalidRequestValueException() | |
10 | + { | |
11 | + super(RequestFailureReason.BAD_PARAMS); | |
12 | + } | |
13 | + | |
14 | + public InvalidRequestValueException(String message) | |
15 | + { | |
16 | + super(RequestFailureReason.BAD_PARAMS, message); | |
17 | + } | |
18 | + | |
19 | + public InvalidRequestValueException(Throwable cause) | |
20 | + { | |
21 | + super(RequestFailureReason.BAD_PARAMS, cause); | |
22 | + } | |
23 | + | |
24 | + public InvalidRequestValueException(String message, Throwable cause) | |
25 | + { | |
26 | + super(RequestFailureReason.BAD_PARAMS, message, cause); | |
27 | + } | |
28 | +} | ... | ... |
src/client/java/com/mumfrey/webprefs/exceptions/InvalidResponseException.java
0 → 100644
1 | +package com.mumfrey.webprefs.exceptions; | |
2 | + | |
3 | +import com.mumfrey.webprefs.framework.RequestFailureReason; | |
4 | + | |
5 | +public class InvalidResponseException extends RuntimeException | |
6 | +{ | |
7 | + private static final long serialVersionUID = 1L; | |
8 | + | |
9 | + private final RequestFailureReason response; | |
10 | + | |
11 | + public InvalidResponseException(RequestFailureReason response) | |
12 | + { | |
13 | + this.response = response; | |
14 | + } | |
15 | + | |
16 | + public InvalidResponseException(RequestFailureReason response, String message) | |
17 | + { | |
18 | + super(message); | |
19 | + this.response = response; | |
20 | + } | |
21 | + | |
22 | + public InvalidResponseException(RequestFailureReason response, Throwable cause) | |
23 | + { | |
24 | + super(cause); | |
25 | + this.response = response; | |
26 | + } | |
27 | + | |
28 | + public InvalidResponseException(RequestFailureReason response, String message, Throwable cause) | |
29 | + { | |
30 | + super(message, cause); | |
31 | + this.response = response; | |
32 | + } | |
33 | + | |
34 | + public RequestFailureReason getReason() | |
35 | + { | |
36 | + return this.response; | |
37 | + } | |
38 | +} | ... | ... |
src/client/java/com/mumfrey/webprefs/exceptions/InvalidServiceException.java
0 → 100644
1 | +package com.mumfrey.webprefs.exceptions; | |
2 | + | |
3 | +public class InvalidServiceException extends RuntimeException | |
4 | +{ | |
5 | + private static final long serialVersionUID = 1L; | |
6 | + | |
7 | + public InvalidServiceException() | |
8 | + { | |
9 | + } | |
10 | + | |
11 | + public InvalidServiceException(String message) | |
12 | + { | |
13 | + super(message); | |
14 | + } | |
15 | + | |
16 | + public InvalidServiceException(Throwable cause) | |
17 | + { | |
18 | + super(cause); | |
19 | + } | |
20 | + | |
21 | + public InvalidServiceException(String message, Throwable cause) | |
22 | + { | |
23 | + super(message, cause); | |
24 | + } | |
25 | + | |
26 | +} | ... | ... |
src/client/java/com/mumfrey/webprefs/exceptions/InvalidUUIDException.java
0 → 100644
1 | +package com.mumfrey.webprefs.exceptions; | |
2 | + | |
3 | +public class InvalidUUIDException extends RuntimeException | |
4 | +{ | |
5 | + private static final long serialVersionUID = 1L; | |
6 | + | |
7 | + public InvalidUUIDException() | |
8 | + { | |
9 | + } | |
10 | + | |
11 | + public InvalidUUIDException(String message) | |
12 | + { | |
13 | + super(message); | |
14 | + } | |
15 | + | |
16 | + public InvalidUUIDException(Throwable cause) | |
17 | + { | |
18 | + super(cause); | |
19 | + } | |
20 | + | |
21 | + public InvalidUUIDException(String message, Throwable cause) | |
22 | + { | |
23 | + super(message, cause); | |
24 | + } | |
25 | +} | ... | ... |
src/client/java/com/mumfrey/webprefs/exceptions/InvalidValueException.java
0 → 100644
1 | +package com.mumfrey.webprefs.exceptions; | |
2 | + | |
3 | +public class InvalidValueException extends RuntimeException | |
4 | +{ | |
5 | + private static final long serialVersionUID = 1L; | |
6 | + | |
7 | + public InvalidValueException() | |
8 | + { | |
9 | + } | |
10 | + | |
11 | + public InvalidValueException(String message) | |
12 | + { | |
13 | + super(message); | |
14 | + } | |
15 | + | |
16 | + public InvalidValueException(Throwable cause) | |
17 | + { | |
18 | + super(cause); | |
19 | + } | |
20 | + | |
21 | + public InvalidValueException(String message, Throwable cause) | |
22 | + { | |
23 | + super(message, cause); | |
24 | + } | |
25 | +} | ... | ... |
src/client/java/com/mumfrey/webprefs/exceptions/ReadOnlyPreferencesException.java
0 → 100644
1 | +package com.mumfrey.webprefs.exceptions; | |
2 | + | |
3 | +public class ReadOnlyPreferencesException extends InvalidPreferenceOperationException | |
4 | +{ | |
5 | + private static final long serialVersionUID = 1L; | |
6 | + | |
7 | + public ReadOnlyPreferencesException() | |
8 | + { | |
9 | + } | |
10 | + | |
11 | + public ReadOnlyPreferencesException(String message) | |
12 | + { | |
13 | + super(message); | |
14 | + } | |
15 | + | |
16 | + public ReadOnlyPreferencesException(Throwable cause) | |
17 | + { | |
18 | + super(cause); | |
19 | + } | |
20 | + | |
21 | + public ReadOnlyPreferencesException(String message, Throwable cause) | |
22 | + { | |
23 | + super(message, cause); | |
24 | + } | |
25 | +} | ... | ... |
src/client/java/com/mumfrey/webprefs/framework/RequestFailureReason.java
0 → 100644
1 | +package com.mumfrey.webprefs.framework; | |
2 | + | |
3 | +public enum RequestFailureReason | |
4 | +{ | |
5 | + UNKNOWN(1), | |
6 | + BAD_PARAMS(1), | |
7 | + NO_SESSION(100), | |
8 | + SERVER_ERROR(3), | |
9 | + UNAUTHORISED(5), | |
10 | + THROTTLED(2), | |
11 | + UUID_MISMATCH(10), | |
12 | + BAD_DATA(1); | |
13 | + | |
14 | + private final int severity; | |
15 | + | |
16 | + private RequestFailureReason(int severity) | |
17 | + { | |
18 | + this.severity = severity; | |
19 | + } | |
20 | + | |
21 | + public int getSeverity() | |
22 | + { | |
23 | + return this.severity; | |
24 | + } | |
25 | + | |
26 | + public boolean isPermanent() | |
27 | + { | |
28 | + return this.severity > 99; | |
29 | + } | |
30 | +} | ... | ... |
src/client/java/com/mumfrey/webprefs/framework/WebPreferencesProvider.java
0 → 100644
1 | +package com.mumfrey.webprefs.framework; | |
2 | + | |
3 | +import java.net.Proxy; | |
4 | +import java.util.Map; | |
5 | +import java.util.Set; | |
6 | +import java.util.concurrent.BlockingQueue; | |
7 | +import java.util.concurrent.LinkedBlockingQueue; | |
8 | + | |
9 | +import net.minecraft.util.Session; | |
10 | + | |
11 | +import com.mumfrey.liteloader.util.log.LiteLoaderLogger; | |
12 | +import com.mumfrey.webprefs.interfaces.IWebPreferencesClient; | |
13 | +import com.mumfrey.webprefs.interfaces.IWebPreferencesProvider; | |
14 | +import com.mumfrey.webprefs.interfaces.IWebPreferencesService; | |
15 | +import com.mumfrey.webprefs.interfaces.IWebPreferencesServiceMonitor; | |
16 | + | |
17 | +public class WebPreferencesProvider extends Thread implements IWebPreferencesProvider, IWebPreferencesServiceMonitor | |
18 | +{ | |
19 | + private final IWebPreferencesService service; | |
20 | + | |
21 | + private final String hostName; | |
22 | + | |
23 | + private final Session session; | |
24 | + | |
25 | + private final int failureThreshold; | |
26 | + | |
27 | + private int failureCount = 0; | |
28 | + | |
29 | + private volatile boolean active = true; | |
30 | + | |
31 | + private final BlockingQueue<WebPreferencesServiceTask> tasks = new LinkedBlockingQueue<WebPreferencesServiceTask>(2048); | |
32 | + | |
33 | + public WebPreferencesProvider(Proxy proxy, Session session, String hostName, int maxFailedRequestsCount) | |
34 | + { | |
35 | + this.service = new WebPreferencesService(proxy, session); | |
36 | + this.service.addMonitor(this); | |
37 | + | |
38 | + this.hostName = hostName; | |
39 | + this.session = session; | |
40 | + this.failureThreshold = maxFailedRequestsCount; | |
41 | + | |
42 | + this.setName("WebPreferencesProvider daemon thread [" + hostName + "]"); | |
43 | + this.setDaemon(true); | |
44 | + this.start(); | |
45 | + } | |
46 | + | |
47 | + @Override | |
48 | + public boolean isActive() | |
49 | + { | |
50 | + return this.active; | |
51 | + } | |
52 | + | |
53 | + public void onTick() | |
54 | + { | |
55 | + } | |
56 | + | |
57 | + @Override | |
58 | + public void run() | |
59 | + { | |
60 | + try | |
61 | + { | |
62 | + while (this.active) | |
63 | + { | |
64 | + WebPreferencesServiceTask task = this.tasks.take(); | |
65 | + try | |
66 | + { | |
67 | + LiteLoaderLogger.debug("WebPreferencesProvider [%s] is processing %s for %s", this.hostName, | |
68 | + task.getClass().getSimpleName(), task.getRequest().getUUID()); | |
69 | + this.service.submit(task.getRequest()); | |
70 | + } | |
71 | + catch (Throwable th) | |
72 | + { | |
73 | + if (th instanceof InterruptedException) throw (InterruptedException)th; | |
74 | + th.printStackTrace(); | |
75 | + | |
76 | + this.onRequestFailed(th, 1); | |
77 | + } | |
78 | + } | |
79 | + } | |
80 | + catch (InterruptedException ex) | |
81 | + { | |
82 | + ex.printStackTrace(); | |
83 | + } | |
84 | + } | |
85 | + | |
86 | + @Override | |
87 | + public void onKeyRequestFailed() | |
88 | + { | |
89 | + this.registerError(this.failureThreshold / 2); | |
90 | + } | |
91 | + | |
92 | + @Override | |
93 | + public void onRequestFailed(Throwable th, int severity) | |
94 | + { | |
95 | + this.registerError(severity); | |
96 | + } | |
97 | + | |
98 | + private void registerError(int severity) | |
99 | + { | |
100 | + this.failureCount += severity; | |
101 | + if (this.failureCount >= this.failureThreshold) | |
102 | + { | |
103 | + LiteLoaderLogger.warning("WebPreferencesProvider for " + this.hostName + " is terminating. Too many failed requests."); | |
104 | + this.active = false; | |
105 | + this.tasks.clear(); | |
106 | + this.interrupt(); | |
107 | + } | |
108 | + } | |
109 | + | |
110 | + @Override | |
111 | + public boolean requestGet(IWebPreferencesClient client, String uuid, Set<String> keys, boolean getPrivate) | |
112 | + { | |
113 | + if (!this.isActive()) | |
114 | + { | |
115 | + return false; | |
116 | + } | |
117 | + | |
118 | + WebPreferencesServiceTask task = new WebPreferencesServiceTaskGet(this, client); | |
119 | + task.setRequest(new WebPreferencesRequestGet(task, uuid, keys, getPrivate)); | |
120 | + return this.tasks.offer(task); | |
121 | + } | |
122 | + | |
123 | + @Override | |
124 | + public boolean requestSet(IWebPreferencesClient client, String uuid, Map<String, String> values, boolean setPrivate) | |
125 | + { | |
126 | + if (!this.isActive()) | |
127 | + { | |
128 | + return false; | |
129 | + } | |
130 | + | |
131 | + WebPreferencesServiceTask task = new WebPreferencesServiceTaskSet(this, client); | |
132 | + task.setRequest(new WebPreferencesRequestSet(task, uuid, values, setPrivate)); | |
133 | + return this.tasks.offer(task); | |
134 | + } | |
135 | + | |
136 | + @Override | |
137 | + public String getHostName() | |
138 | + { | |
139 | + return this.hostName; | |
140 | + } | |
141 | + | |
142 | + @Override | |
143 | + public Session getSession() | |
144 | + { | |
145 | + return this.session; | |
146 | + } | |
147 | + | |
148 | + @Override | |
149 | + public IWebPreferencesService getService() | |
150 | + { | |
151 | + return this.service; | |
152 | + } | |
153 | +} | ... | ... |
src/client/java/com/mumfrey/webprefs/framework/WebPreferencesRequestAbstract.java
0 → 100644
1 | +package com.mumfrey.webprefs.framework; | |
2 | + | |
3 | +import java.net.URI; | |
4 | +import java.util.HashMap; | |
5 | +import java.util.Map; | |
6 | +import java.util.regex.Pattern; | |
7 | + | |
8 | +import net.minecraft.util.Session; | |
9 | + | |
10 | +import com.google.gson.Gson; | |
11 | +import com.mumfrey.webprefs.exceptions.InvalidRequestException; | |
12 | +import com.mumfrey.webprefs.exceptions.InvalidRequestKeyException; | |
13 | +import com.mumfrey.webprefs.exceptions.InvalidRequestValueException; | |
14 | +import com.mumfrey.webprefs.exceptions.InvalidResponseException; | |
15 | +import com.mumfrey.webprefs.interfaces.IWebPreferencesRequest; | |
16 | +import com.mumfrey.webprefs.interfaces.IWebPreferencesResponse; | |
17 | +import com.mumfrey.webprefs.interfaces.IWebPreferencesServiceDelegate; | |
18 | + | |
19 | +abstract class WebPreferencesRequestAbstract implements IWebPreferencesRequest | |
20 | +{ | |
21 | + private static final long serialVersionUID = 1L; | |
22 | + | |
23 | + private static final Pattern keyPattern = Pattern.compile("^[a-z0-9_\\-\\.]{1,32}$"); | |
24 | + | |
25 | + private static final Gson gson = new Gson(); | |
26 | + | |
27 | + private final transient URI uri; | |
28 | + | |
29 | + private final transient IWebPreferencesServiceDelegate delegate; | |
30 | + | |
31 | + private final transient String uuid; | |
32 | + | |
33 | + public WebPreferencesRequestAbstract(IWebPreferencesServiceDelegate delegate, String uuid) | |
34 | + { | |
35 | + if (delegate == null) | |
36 | + { | |
37 | + throw new IllegalArgumentException("Attempted to create a request with no delegate"); | |
38 | + } | |
39 | + | |
40 | + this.uri = URI.create(String.format("http://%s%s", delegate.getHostName(), this.getPath())); | |
41 | + | |
42 | + this.delegate = delegate; | |
43 | + this.uuid = uuid; | |
44 | + } | |
45 | + | |
46 | + protected abstract String getPath(); | |
47 | + | |
48 | + @Override | |
49 | + public IWebPreferencesServiceDelegate getDelegate() | |
50 | + { | |
51 | + return this.delegate; | |
52 | + } | |
53 | + | |
54 | + @Override | |
55 | + public URI getRequestURI() | |
56 | + { | |
57 | + return this.uri; | |
58 | + } | |
59 | + | |
60 | + @Override | |
61 | + public String getUUID() | |
62 | + { | |
63 | + return this.uuid; | |
64 | + } | |
65 | + | |
66 | + @Override | |
67 | + public Map<String, String> getPostVars() | |
68 | + { | |
69 | + Map<String, String> params = new HashMap<String, String>(); | |
70 | + this.addParams(params); | |
71 | + return params; | |
72 | + } | |
73 | + | |
74 | + protected void addParams(Map<String, String> params) | |
75 | + { | |
76 | + if (this.isValidationRequired()) | |
77 | + { | |
78 | + Session session = this.getDelegate().getSession(); | |
79 | + if (session == null) | |
80 | + { | |
81 | + throw new InvalidRequestException(RequestFailureReason.NO_SESSION, "Request has no session"); | |
82 | + } | |
83 | + | |
84 | + params.put("u", session.getUsername()); | |
85 | + } | |
86 | + | |
87 | + params.put("i", this.uuid); | |
88 | + params.put("j", this.toJson()); | |
89 | + } | |
90 | + | |
91 | + @Override | |
92 | + public final void onReceivedResponse(IWebPreferencesResponse response) | |
93 | + { | |
94 | + if (response == null) | |
95 | + { | |
96 | + throw new InvalidResponseException(null, "Error reading server response"); | |
97 | + } | |
98 | + | |
99 | + if (response.getResponse().startsWith("500")) | |
100 | + { | |
101 | + throw new InvalidResponseException(RequestFailureReason.SERVER_ERROR, | |
102 | + "The server returned an invalid resonse: " + response.getResponse(), response.getThrowable()); | |
103 | + } | |
104 | + | |
105 | + if (!response.getResponse().startsWith("200")) | |
106 | + { | |
107 | + RequestFailureReason reason = RequestFailureReason.UNKNOWN; | |
108 | + | |
109 | + if (response.getResponse().startsWith("429")) reason = RequestFailureReason.THROTTLED; | |
110 | + if (response.getResponse().startsWith("401")) reason = RequestFailureReason.UNAUTHORISED; | |
111 | + | |
112 | + String message = response.getMessage(); | |
113 | + throw new InvalidResponseException(reason, | |
114 | + "The server responsed with " + response.getResponse() + (message != null ? " \"" + message + "\"" : "")); | |
115 | + } | |
116 | + | |
117 | + if (!this.getUUID().equals(response.getUUID())) | |
118 | + { | |
119 | + throw new InvalidResponseException(RequestFailureReason.UUID_MISMATCH, "The response UUID did not match the request"); | |
120 | + } | |
121 | + | |
122 | + this.validateResponse(response); | |
123 | + } | |
124 | + | |
125 | + protected abstract void validateResponse(IWebPreferencesResponse response); | |
126 | + | |
127 | + protected final void validateKey(String key) | |
128 | + { | |
129 | + if (key == null || !WebPreferencesRequestAbstract.keyPattern.matcher(key).matches()) | |
130 | + { | |
131 | + throw new InvalidRequestKeyException("The specified key [" + key + "] is not valid"); | |
132 | + } | |
133 | + } | |
134 | + | |
135 | + protected final void validateValue(String key, String value) | |
136 | + { | |
137 | + if (value == null || value.length() > 255) | |
138 | + { | |
139 | + throw new InvalidRequestValueException("The specified value [" + value + "] for key [" + key + "] is not valid"); | |
140 | + } | |
141 | + } | |
142 | + | |
143 | + public String toJson() | |
144 | + { | |
145 | + return WebPreferencesRequestAbstract.gson.toJson(this); | |
146 | + } | |
147 | + | |
148 | + @Override | |
149 | + public String toString() | |
150 | + { | |
151 | + try | |
152 | + { | |
153 | + return WebPreferencesRequestAbstract.gson.toJson(this); | |
154 | + } | |
155 | + catch (Throwable th) | |
156 | + { | |
157 | + return "{\"Invalid JSON\"}"; | |
158 | + } | |
159 | + } | |
160 | +} | ... | ... |
src/client/java/com/mumfrey/webprefs/framework/WebPreferencesRequestGet.java
0 → 100644
1 | +package com.mumfrey.webprefs.framework; | |
2 | + | |
3 | +import java.util.HashSet; | |
4 | +import java.util.Set; | |
5 | + | |
6 | +import com.google.gson.annotations.Expose; | |
7 | +import com.google.gson.annotations.SerializedName; | |
8 | +import com.mumfrey.webprefs.exceptions.InvalidRequestException; | |
9 | +import com.mumfrey.webprefs.exceptions.InvalidResponseException; | |
10 | +import com.mumfrey.webprefs.interfaces.IWebPreferencesResponse; | |
11 | +import com.mumfrey.webprefs.interfaces.IWebPreferencesServiceDelegate; | |
12 | + | |
13 | +class WebPreferencesRequestGet extends WebPreferencesRequestAbstract | |
14 | +{ | |
15 | + private static final long serialVersionUID = 1L; | |
16 | + | |
17 | + @Expose @SerializedName("get") | |
18 | + private final Set<String> keys = new HashSet<String>(); | |
19 | + | |
20 | + @Expose @SerializedName("private") | |
21 | + private boolean isPrivate; | |
22 | + | |
23 | + public WebPreferencesRequestGet(IWebPreferencesServiceDelegate delegate, String uuid, Set<String> keys) | |
24 | + { | |
25 | + this(delegate, uuid, keys, false); | |
26 | + } | |
27 | + | |
28 | + public WebPreferencesRequestGet(IWebPreferencesServiceDelegate delegate, String uuid, Set<String> keys, boolean isPrivate) | |
29 | + { | |
30 | + super(delegate, uuid); | |
31 | + | |
32 | + if (isPrivate && delegate.getSession() == null) | |
33 | + { | |
34 | + throw new InvalidRequestException(RequestFailureReason.NO_SESSION, "Cannot request private values without supplying a session"); | |
35 | + } | |
36 | + | |
37 | + this.validate(keys); | |
38 | + | |
39 | + this.keys.addAll(keys); | |
40 | + this.isPrivate = isPrivate; | |
41 | + } | |
42 | + | |
43 | + @Override | |
44 | + protected String getPath() | |
45 | + { | |
46 | + return "/get"; | |
47 | + } | |
48 | + | |
49 | + @Override | |
50 | + public boolean isValidationRequired() | |
51 | + { | |
52 | + return this.isPrivate; | |
53 | + } | |
54 | + | |
55 | + @Override | |
56 | + public Set<String> getKeys() | |
57 | + { | |
58 | + return this.keys; | |
59 | + } | |
60 | + | |
61 | + @Override | |
62 | + protected void validateResponse(IWebPreferencesResponse response) | |
63 | + { | |
64 | + if (response.hasValues()) | |
65 | + { | |
66 | + Set<String> responseKeys = response.getValues().keySet(); | |
67 | + for (String key : this.keys) | |
68 | + { | |
69 | + if (!responseKeys.contains(key)) | |
70 | + { | |
71 | + throw new InvalidResponseException(RequestFailureReason.BAD_DATA, | |
72 | + "The server responded with an incomplete key set, missing key [" + key + "]"); | |
73 | + } | |
74 | + } | |
75 | + } | |
76 | + } | |
77 | + | |
78 | + private void validate(Set<String> keys) | |
79 | + { | |
80 | + if (keys == null || keys.isEmpty()) | |
81 | + { | |
82 | + throw new InvalidRequestException(RequestFailureReason.BAD_PARAMS, "Cannot request an empty set"); | |
83 | + } | |
84 | + | |
85 | + for (String key : keys) | |
86 | + { | |
87 | + this.validateKey(key); | |
88 | + } | |
89 | + } | |
90 | +} | ... | ... |
src/client/java/com/mumfrey/webprefs/framework/WebPreferencesRequestKey.java
0 → 100644
1 | +package com.mumfrey.webprefs.framework; | |
2 | + | |
3 | +import java.util.HashSet; | |
4 | +import java.util.Set; | |
5 | + | |
6 | +import net.minecraft.util.Session; | |
7 | + | |
8 | +import com.mumfrey.webprefs.interfaces.IWebPreferencesRequest; | |
9 | +import com.mumfrey.webprefs.interfaces.IWebPreferencesResponse; | |
10 | +import com.mumfrey.webprefs.interfaces.IWebPreferencesServiceDelegate; | |
11 | + | |
12 | +public class WebPreferencesRequestKey extends WebPreferencesRequestAbstract | |
13 | +{ | |
14 | + private static final long serialVersionUID = 1L; | |
15 | + | |
16 | + protected final transient WebPreferencesService server; | |
17 | + | |
18 | + public WebPreferencesRequestKey(final WebPreferencesService server, final Session session, final String hostName) | |
19 | + { | |
20 | + super(new IWebPreferencesServiceDelegate() | |
21 | + { | |
22 | + @Override | |
23 | + public void onRequestFailed(IWebPreferencesRequest request, Throwable th, RequestFailureReason reason) | |
24 | + { | |
25 | + server.handleKeyRequestFailed(th); | |
26 | + } | |
27 | + | |
28 | + @Override | |
29 | + public void onReceivedResponse(IWebPreferencesRequest request, IWebPreferencesResponse response) | |
30 | + { | |
31 | + server.handleKeyRequestCompleted(response); | |
32 | + } | |
33 | + | |
34 | + @Override | |
35 | + public Session getSession() | |
36 | + { | |
37 | + return session; | |
38 | + } | |
39 | + | |
40 | + @Override | |
41 | + public String getHostName() | |
42 | + { | |
43 | + return hostName; | |
44 | + } | |
45 | + }, session.getPlayerID()); | |
46 | + | |
47 | + this.server = server; | |
48 | + } | |
49 | + | |
50 | + @Override | |
51 | + public boolean isValidationRequired() | |
52 | + { | |
53 | + return true; | |
54 | + } | |
55 | + | |
56 | + @Override | |
57 | + protected String getPath() | |
58 | + { | |
59 | + return "/key"; | |
60 | + } | |
61 | + | |
62 | + @Override | |
63 | + protected void validateResponse(IWebPreferencesResponse response) | |
64 | + { | |
65 | + } | |
66 | + | |
67 | + @Override | |
68 | + public Set<String> getKeys() | |
69 | + { | |
70 | + return new HashSet<String>(); | |
71 | + } | |
72 | +} | ... | ... |
src/client/java/com/mumfrey/webprefs/framework/WebPreferencesRequestSet.java
0 → 100644
1 | +package com.mumfrey.webprefs.framework; | |
2 | + | |
3 | +import java.util.HashMap; | |
4 | +import java.util.Map; | |
5 | +import java.util.Map.Entry; | |
6 | +import java.util.Set; | |
7 | + | |
8 | +import com.google.gson.annotations.Expose; | |
9 | +import com.google.gson.annotations.SerializedName; | |
10 | +import com.mumfrey.webprefs.exceptions.InvalidRequestException; | |
11 | +import com.mumfrey.webprefs.exceptions.InvalidResponseException; | |
12 | +import com.mumfrey.webprefs.interfaces.IWebPreferencesResponse; | |
13 | +import com.mumfrey.webprefs.interfaces.IWebPreferencesServiceDelegate; | |
14 | + | |
15 | +class WebPreferencesRequestSet extends WebPreferencesRequestAbstract | |
16 | +{ | |
17 | + private static final long serialVersionUID = 1L; | |
18 | + | |
19 | + @Expose @SerializedName("set") | |
20 | + private final Map<String, String> map = new HashMap<String, String>(); | |
21 | + | |
22 | + @Expose @SerializedName("private") | |
23 | + private boolean isPrivate; | |
24 | + | |
25 | + public WebPreferencesRequestSet(IWebPreferencesServiceDelegate delegate, String uuid, Map<String, String> values) | |
26 | + { | |
27 | + this(delegate, uuid, values, false); | |
28 | + } | |
29 | + | |
30 | + public WebPreferencesRequestSet(IWebPreferencesServiceDelegate delegate, String uuid, Map<String, String> values, boolean isPrivate) | |
31 | + { | |
32 | + super(delegate, uuid); | |
33 | + | |
34 | + if (isPrivate && delegate.getSession() == null) | |
35 | + { | |
36 | + throw new InvalidRequestException(RequestFailureReason.NO_SESSION, "Cannot request private values without supplying a session"); | |
37 | + } | |
38 | + | |
39 | + this.validate(values); | |
40 | + | |
41 | + this.map.putAll(values); | |
42 | + this.isPrivate = isPrivate; | |
43 | + } | |
44 | + | |
45 | + @Override | |
46 | + protected String getPath() | |
47 | + { | |
48 | + return "/set"; | |
49 | + } | |
50 | + | |
51 | + @Override | |
52 | + public boolean isValidationRequired() | |
53 | + { | |
54 | + return true; | |
55 | + } | |
56 | + | |
57 | + @Override | |
58 | + public Set<String> getKeys() | |
59 | + { | |
60 | + return this.map.keySet(); | |
61 | + } | |
62 | + | |
63 | + public Map<String, String> getMap() | |
64 | + { | |
65 | + return this.map; | |
66 | + } | |
67 | + | |
68 | + @Override | |
69 | + protected void validateResponse(IWebPreferencesResponse response) | |
70 | + { | |
71 | + if (response.hasSetters()) | |
72 | + { | |
73 | + Set<String> responseKeys = response.getSetters(); | |
74 | + for (String key : this.map.keySet()) | |
75 | + { | |
76 | + if (!responseKeys.contains(key)) | |
77 | + { | |
78 | + throw new InvalidResponseException(RequestFailureReason.BAD_DATA, | |
79 | + "The server responded with an incomplete key set, missing key [" + key + "]"); | |
80 | + } | |
81 | + } | |
82 | + } | |
83 | + } | |
84 | + | |
85 | + private void validate(Map<String, String> set) | |
86 | + { | |
87 | + for (Entry<String, String> entry : set.entrySet()) | |
88 | + { | |
89 | + this.validateKey(entry.getKey()); | |
90 | + this.validateValue(entry.getKey(), entry.getValue()); | |
91 | + } | |
92 | + } | |
93 | +} | ... | ... |
src/client/java/com/mumfrey/webprefs/framework/WebPreferencesResponse.java
0 → 100644
1 | +package com.mumfrey.webprefs.framework; | |
2 | + | |
3 | +import java.util.HashSet; | |
4 | +import java.util.List; | |
5 | +import java.util.Map; | |
6 | +import java.util.Set; | |
7 | + | |
8 | +import com.google.gson.Gson; | |
9 | +import com.google.gson.GsonBuilder; | |
10 | +import com.google.gson.JsonSyntaxException; | |
11 | +import com.google.gson.annotations.Expose; | |
12 | +import com.google.gson.annotations.SerializedName; | |
13 | +import com.mumfrey.webprefs.interfaces.IWebPreferencesResponse; | |
14 | + | |
15 | +class WebPreferencesResponse implements IWebPreferencesResponse | |
16 | +{ | |
17 | + private static final long serialVersionUID = 1L; | |
18 | + | |
19 | + private static final Gson gson = new GsonBuilder().setPrettyPrinting().create(); | |
20 | + | |
21 | + @Expose @SerializedName("response") | |
22 | + private String response; | |
23 | + | |
24 | + @Expose @SerializedName("message") | |
25 | + private String message; | |
26 | + | |
27 | + @Expose @SerializedName("uuid") | |
28 | + private String uuid; | |
29 | + | |
30 | + @Expose @SerializedName("serverid") | |
31 | + private String serverId; | |
32 | + | |
33 | + @Expose @SerializedName("rate") | |
34 | + private int rateLimit; | |
35 | + | |
36 | + @Expose @SerializedName("get") | |
37 | + private Map<String, String> get; | |
38 | + | |
39 | + @Expose @SerializedName("set") | |
40 | + private List<String> set; | |
41 | + | |
42 | + private transient Throwable th; | |
43 | + | |
44 | + public WebPreferencesResponse() {} | |
45 | + | |
46 | + private WebPreferencesResponse(String response, Throwable th) | |
47 | + { | |
48 | + this.response = response; | |
49 | + this.th = th; | |
50 | + } | |
51 | + | |
52 | + @Override | |
53 | + public String getResponse() | |
54 | + { | |
55 | + return this.response; | |
56 | + } | |
57 | + | |
58 | + @Override | |
59 | + public String getMessage() | |
60 | + { | |
61 | + return this.message; | |
62 | + } | |
63 | + | |
64 | + @Override | |
65 | + public Throwable getThrowable() | |
66 | + { | |
67 | + return this.th; | |
68 | + } | |
69 | + | |
70 | + @Override | |
71 | + public String getUUID() | |
72 | + { | |
73 | + return this.uuid; | |
74 | + } | |
75 | + | |
76 | + @Override | |
77 | + public String getServerId() | |
78 | + { | |
79 | + return this.serverId; | |
80 | + } | |
81 | + | |
82 | + @Override | |
83 | + public boolean hasValues() | |
84 | + { | |
85 | + return this.get != null; | |
86 | + } | |
87 | + | |
88 | + @Override | |
89 | + public Map<String, String> getValues() | |
90 | + { | |
91 | + return this.get; | |
92 | + } | |
93 | + | |
94 | + @Override | |
95 | + public boolean hasSetters() | |
96 | + { | |
97 | + return this.set != null; | |
98 | + } | |
99 | + | |
100 | + @Override | |
101 | + public Set<String> getSetters() | |
102 | + { | |
103 | + return new HashSet<String>(this.set); | |
104 | + } | |
105 | + | |
106 | + public static IWebPreferencesResponse fromJson(String json) | |
107 | + { | |
108 | + try | |
109 | + { | |
110 | + return WebPreferencesResponse.gson.fromJson(json, WebPreferencesResponse.class); | |
111 | + } | |
112 | + catch (JsonSyntaxException ex) | |
113 | + { | |
114 | + return new WebPreferencesResponse("500 Invalid JSON", ex); | |
115 | + } | |
116 | + catch (Throwable th) | |
117 | + { | |
118 | + return new WebPreferencesResponse("500 Invalid JSON", th); | |
119 | + } | |
120 | + } | |
121 | + | |
122 | + @Override | |
123 | + public String toString() | |
124 | + { | |
125 | + try | |
126 | + { | |
127 | + return WebPreferencesResponse.gson.toJson(this); | |
128 | + } | |
129 | + catch (Throwable th) | |
130 | + { | |
131 | + return "{\"Invalid JSON\"}"; | |
132 | + } | |
133 | + } | |
134 | +} | ... | ... |
src/client/java/com/mumfrey/webprefs/framework/WebPreferencesService.java
0 → 100644
1 | +package com.mumfrey.webprefs.framework; | |
2 | + | |
3 | +import java.io.BufferedReader; | |
4 | +import java.io.IOException; | |
5 | +import java.io.InputStream; | |
6 | +import java.io.InputStreamReader; | |
7 | +import java.io.OutputStream; | |
8 | +import java.io.UnsupportedEncodingException; | |
9 | +import java.net.HttpURLConnection; | |
10 | +import java.net.Proxy; | |
11 | +import java.net.URI; | |
12 | +import java.net.URL; | |
13 | +import java.net.URLEncoder; | |
14 | +import java.util.ArrayList; | |
15 | +import java.util.HashMap; | |
16 | +import java.util.List; | |
17 | +import java.util.Map; | |
18 | +import java.util.Map.Entry; | |
19 | + | |
20 | +import net.minecraft.util.Session; | |
21 | + | |
22 | +import org.apache.commons.io.IOUtils; | |
23 | + | |
24 | +import com.google.common.base.Charsets; | |
25 | +import com.google.gson.Gson; | |
26 | +import com.mumfrey.liteloader.util.log.LiteLoaderLogger; | |
27 | +import com.mumfrey.webprefs.exceptions.InvalidRequestException; | |
28 | +import com.mumfrey.webprefs.exceptions.InvalidResponseException; | |
29 | +import com.mumfrey.webprefs.interfaces.IWebPreferencesRequest; | |
30 | +import com.mumfrey.webprefs.interfaces.IWebPreferencesResponse; | |
31 | +import com.mumfrey.webprefs.interfaces.IWebPreferencesService; | |
32 | +import com.mumfrey.webprefs.interfaces.IWebPreferencesServiceMonitor; | |
33 | + | |
34 | +class WebPreferencesService implements IWebPreferencesService | |
35 | +{ | |
36 | + private static final int TIMEOUT_MSEC = 5000; | |
37 | + | |
38 | + private final Proxy proxy; | |
39 | + | |
40 | + private final Session session; | |
41 | + | |
42 | + private final Map<String, String> serverKeys = new HashMap<String, String>(); | |
43 | + | |
44 | + private final List<IWebPreferencesServiceMonitor> monitors = new ArrayList<IWebPreferencesServiceMonitor>(); | |
45 | + | |
46 | + private long lastMojangAuth = 0L; | |
47 | + | |
48 | + WebPreferencesService(Proxy proxy, Session session) | |
49 | + { | |
50 | + this.proxy = proxy; | |
51 | + this.session = session; | |
52 | + } | |
53 | + | |
54 | + @Override | |
55 | + public void addMonitor(IWebPreferencesServiceMonitor monitor) | |
56 | + { | |
57 | + if (!this.monitors.contains(monitor)) | |
58 | + { | |
59 | + this.monitors.add(monitor); | |
60 | + } | |
61 | + } | |
62 | + | |
63 | + void handleKeyRequestFailed(Throwable th) | |
64 | + { | |
65 | + LiteLoaderLogger.debug(th, "Key request failed with message %s", th.getMessage()); | |
66 | + | |
67 | + for (IWebPreferencesServiceMonitor monitor : this.monitors) | |
68 | + { | |
69 | + monitor.onKeyRequestFailed(); | |
70 | + } | |
71 | + } | |
72 | + | |
73 | + void handleKeyRequestCompleted(IWebPreferencesResponse response) | |
74 | + { | |
75 | + } | |
76 | + | |
77 | + @Override | |
78 | + public void submit(IWebPreferencesRequest request) | |
79 | + { | |
80 | + try | |
81 | + { | |
82 | + this.beginProcessingRequest(request); | |
83 | + } | |
84 | + catch (InvalidRequestException ex) | |
85 | + { | |
86 | + request.getDelegate().onRequestFailed(request, ex, ex.getReason()); | |
87 | + } | |
88 | + } | |
89 | + | |
90 | + private IWebPreferencesResponse beginProcessingRequest(IWebPreferencesRequest request) throws InvalidRequestException | |
91 | + { | |
92 | + LiteLoaderLogger.debug("WebPreferencesService is processing %s for %s", request.getClass().getSimpleName(), request.getUUID()); | |
93 | + | |
94 | + if (request.isValidationRequired()) | |
95 | + { | |
96 | + String requestClass = request.getClass().getSimpleName(); | |
97 | + | |
98 | + Session session = request.getDelegate().getSession(); | |
99 | + if (session == null) | |
100 | + { | |
101 | + throw new InvalidRequestException(RequestFailureReason.NO_SESSION, | |
102 | + "Validation is required for " + requestClass + " but no session was provided."); | |
103 | + } | |
104 | + | |
105 | + String serverId = this.getServerIdForRequest(request); | |
106 | + | |
107 | + if (!this.registerServerConnection(session, serverId)) | |
108 | + { | |
109 | + throw new InvalidRequestException(RequestFailureReason.NO_SESSION, | |
110 | + "Validation is required for " + requestClass + " but no session was provided or session validation failed"); | |
111 | + } | |
112 | + } | |
113 | + | |
114 | + return this.processRequest(request); | |
115 | + } | |
116 | + | |
117 | + private IWebPreferencesResponse processRequest(IWebPreferencesRequest request) | |
118 | + { | |
119 | + try | |
120 | + { | |
121 | + String data = this.httpPost(request.getRequestURI(), request.getPostVars()); | |
122 | + IWebPreferencesResponse response = WebPreferencesResponse.fromJson(data); | |
123 | + | |
124 | + LiteLoaderLogger.debug("Response: %s", response); | |
125 | + request.onReceivedResponse(response); | |
126 | + | |
127 | + request.getDelegate().onReceivedResponse(request, response); | |
128 | + return response; | |
129 | + } | |
130 | + catch (InvalidResponseException ex) | |
131 | + { | |
132 | + request.getDelegate().onRequestFailed(request, ex, ex.getReason()); | |
133 | + | |
134 | + for (IWebPreferencesServiceMonitor monitor : this.monitors) | |
135 | + { | |
136 | + monitor.onRequestFailed(ex, ex.getReason().getSeverity()); | |
137 | + } | |
138 | + } | |
139 | + catch (IOException ex) | |
140 | + { | |
141 | + request.getDelegate().onRequestFailed(request, ex, RequestFailureReason.SERVER_ERROR); | |
142 | + | |
143 | + for (IWebPreferencesServiceMonitor monitor : this.monitors) | |
144 | + { | |
145 | + monitor.onRequestFailed(ex, RequestFailureReason.SERVER_ERROR.getSeverity()); | |
146 | + } | |
147 | + } | |
148 | + catch (Exception ex) | |
149 | + { | |
150 | + for (IWebPreferencesServiceMonitor monitor : this.monitors) | |
151 | + { | |
152 | + monitor.onRequestFailed(ex, RequestFailureReason.UNKNOWN.getSeverity()); | |
153 | + } | |
154 | + } | |
155 | + | |
156 | + return null; | |
157 | + } | |
158 | + | |
159 | + private String getServerIdForRequest(IWebPreferencesRequest request) | |
160 | + { | |
161 | + if (request.getDelegate().getSession() == null) | |
162 | + { | |
163 | + return null; | |
164 | + } | |
165 | + | |
166 | + String hostName = request.getDelegate().getHostName(); | |
167 | + String serverId = this.serverKeys.get(hostName); | |
168 | + | |
169 | + if (serverId == null) | |
170 | + { | |
171 | + LiteLoaderLogger.info("Looking up server ID for " + hostName); | |
172 | + WebPreferencesRequestKey keyRequest = new WebPreferencesRequestKey(this, this.session, hostName); | |
173 | + IWebPreferencesResponse response = this.processRequest(keyRequest); | |
174 | + if (response == null || response.getServerId() == null) | |
175 | + { | |
176 | + throw new InvalidRequestException(RequestFailureReason.SERVER_ERROR, "Could not retrieve server ID for " + hostName); | |
177 | + } | |
178 | + | |
179 | + serverId = response.getServerId(); | |
180 | + this.serverKeys.put(hostName, serverId); | |
181 | + | |
182 | + LiteLoaderLogger.info("Got server ID for " + hostName + " [" + serverId + "]"); | |
183 | + } | |
184 | + | |
185 | + return serverId; | |
186 | + } | |
187 | + | |
188 | + public String httpPost(URI uri, Map<String, String> params) throws IOException | |
189 | + { | |
190 | + String query = this.buildQuery(params); | |
191 | + byte[] queryBytes = query.getBytes(Charsets.UTF_8); | |
192 | + | |
193 | + LiteLoaderLogger.debug("Connecting to " + uri); | |
194 | + HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(this.proxy); | |
195 | + http.setConnectTimeout(WebPreferencesService.TIMEOUT_MSEC); | |
196 | + http.setReadTimeout(WebPreferencesService.TIMEOUT_MSEC); | |
197 | + http.setUseCaches(false); | |
198 | + http.setDoOutput(true); | |
199 | + | |
200 | + http.addRequestProperty("Content-type", "application/x-www-form-urlencoded"); | |
201 | + http.setRequestProperty("Content-Length", "" + queryBytes.length); | |
202 | + | |
203 | + OutputStream outputStream = null; | |
204 | + | |
205 | + try | |
206 | + { | |
207 | + outputStream = http.getOutputStream(); | |
208 | + IOUtils.write(queryBytes, outputStream); | |
209 | + } | |
210 | + finally | |
211 | + { | |
212 | + IOUtils.closeQuietly(outputStream); | |
213 | + } | |
214 | + | |
215 | + try | |
216 | + { | |
217 | + String debugMessages = http.getHeaderField("X-Debug-Message"); | |
218 | + if (debugMessages != null) | |
219 | + { | |
220 | + String[] messages = new Gson().fromJson(debugMessages, String[].class); | |
221 | + for (String message : messages) | |
222 | + { | |
223 | + LiteLoaderLogger.debug("[SERVER] %s", message); | |
224 | + } | |
225 | + } | |
226 | + } | |
227 | + catch (Exception ex) {} | |
228 | + | |
229 | + InputStream inputStream = null; | |
230 | + | |
231 | + try | |
232 | + { | |
233 | + try | |
234 | + { | |
235 | + inputStream = http.getInputStream(); | |
236 | + String response = IOUtils.toString(inputStream, Charsets.UTF_8); | |
237 | + return response; | |
238 | + } | |
239 | + catch (IOException ex) | |
240 | + { | |
241 | + IOUtils.closeQuietly(inputStream); | |
242 | + inputStream = http.getErrorStream(); | |
243 | + if (inputStream == null) | |
244 | + { | |
245 | + return this.formatErrorAsJson(http.getResponseCode() + " " + http.getResponseMessage(), ex.getMessage()); | |
246 | + } | |
247 | + | |
248 | + String response = IOUtils.toString(inputStream, Charsets.UTF_8); | |
249 | + | |
250 | + String contentType = http.getHeaderField("Content-type"); | |
251 | + if (!"application/json".equals(contentType)) | |
252 | + { | |
253 | + System.err.println(response); | |
254 | + return this.formatErrorAsJson(http.getResponseCode() + " " + http.getResponseMessage(), "Invalid content type " + contentType); | |
255 | + } | |
256 | + | |
257 | + return response; | |
258 | + } | |
259 | + } | |
260 | + finally | |
261 | + { | |
262 | + IOUtils.closeQuietly(inputStream); | |
263 | + } | |
264 | + } | |
265 | + | |
266 | + private String formatErrorAsJson(String response, String message) | |
267 | + { | |
268 | + return String.format("{\"response\":\"%s\",\"message\":\"%s\"}", response, message); | |
269 | + } | |
270 | + | |
271 | + private String buildQuery(Map<String, String> params) | |
272 | + { | |
273 | + StringBuilder sb = new StringBuilder(); | |
274 | + | |
275 | + try | |
276 | + { | |
277 | + String separator = ""; | |
278 | + for (Entry<String, String> postValue : params.entrySet()) | |
279 | + { | |
280 | + sb.append(separator).append(postValue.getKey()).append("=").append(URLEncoder.encode(postValue.getValue(), "UTF-8")); | |
281 | + separator = "&"; | |
282 | + } | |
283 | + } | |
284 | + catch (UnsupportedEncodingException ex) | |
285 | + { | |
286 | + ex.printStackTrace(); | |
287 | + } | |
288 | + | |
289 | + return sb.toString(); | |
290 | + } | |
291 | + | |
292 | + private boolean registerServerConnection(Session session, String serverId) | |
293 | + { | |
294 | + if (session == null || serverId == null) | |
295 | + { | |
296 | + return false; | |
297 | + } | |
298 | + | |
299 | + if (System.currentTimeMillis() - this.lastMojangAuth < 300000L) | |
300 | + { | |
301 | + LiteLoaderLogger.debug("Mojang connection is still fresh, using existing ticket"); | |
302 | + return true; | |
303 | + } | |
304 | + | |
305 | + try | |
306 | + { | |
307 | + LiteLoaderLogger.debug("Creating Mojang session ticket..."); | |
308 | + URL checkServerUrl = new URL("http://session.minecraft.net/game/joinserver.jsp?user=" + URLEncoder.encode(session.getUsername(), "UTF-8") + "&sessionId=" + URLEncoder.encode(session.getSessionID(), "UTF-8") + "&serverId=" + URLEncoder.encode(serverId, "UTF-8")); | |
309 | + BufferedReader responseReader = new BufferedReader(new InputStreamReader(checkServerUrl.openStream())); | |
310 | + String response = responseReader.readLine(); | |
311 | + responseReader.close(); | |
312 | + boolean joinSuccess = "OK".equals(response); | |
313 | + if (joinSuccess) | |
314 | + { | |
315 | + this.lastMojangAuth = System.currentTimeMillis(); | |
316 | + return true; | |
317 | + } | |
318 | + } | |
319 | + catch (IOException ex) | |
320 | + { | |
321 | + ex.printStackTrace(); | |
322 | + LiteLoaderLogger.debug("Failed to log on to invoke joinserver, connection to mojang failed"); | |
323 | + throw new InvalidRequestException(RequestFailureReason.SERVER_ERROR, "Failed registering server connection with Mojang"); | |
324 | + } | |
325 | + | |
326 | + return false; | |
327 | + } | |
328 | +} | ... | ... |
src/client/java/com/mumfrey/webprefs/framework/WebPreferencesServiceTask.java
0 → 100644
1 | +package com.mumfrey.webprefs.framework; | |
2 | + | |
3 | +import net.minecraft.util.Session; | |
4 | + | |
5 | +import com.mumfrey.webprefs.interfaces.IWebPreferencesClient; | |
6 | +import com.mumfrey.webprefs.interfaces.IWebPreferencesProvider; | |
7 | +import com.mumfrey.webprefs.interfaces.IWebPreferencesRequest; | |
8 | +import com.mumfrey.webprefs.interfaces.IWebPreferencesServiceDelegate; | |
9 | + | |
10 | +abstract class WebPreferencesServiceTask implements IWebPreferencesServiceDelegate | |
11 | +{ | |
12 | + private final IWebPreferencesProvider provider; | |
13 | + | |
14 | + private final IWebPreferencesClient client; | |
15 | + | |
16 | + private IWebPreferencesRequest request; | |
17 | + | |
18 | + WebPreferencesServiceTask(IWebPreferencesProvider provider, IWebPreferencesClient client) | |
19 | + { | |
20 | + this.provider = provider; | |
21 | + this.client = client; | |
22 | + } | |
23 | + | |
24 | + public IWebPreferencesClient getClient() | |
25 | + { | |
26 | + return this.client; | |
27 | + } | |
28 | + | |
29 | + public IWebPreferencesRequest getRequest() | |
30 | + { | |
31 | + return this.request; | |
32 | + } | |
33 | + | |
34 | + public void setRequest(IWebPreferencesRequest request) | |
35 | + { | |
36 | + this.request = request; | |
37 | + } | |
38 | + | |
39 | + @Override | |
40 | + public String getHostName() | |
41 | + { | |
42 | + return this.provider.getHostName(); | |
43 | + } | |
44 | + | |
45 | + @Override | |
46 | + public Session getSession() | |
47 | + { | |
48 | + return this.provider.getSession(); | |
49 | + } | |
50 | + | |
51 | + @Override | |
52 | + public String toString() | |
53 | + { | |
54 | + return String.format("%s[%s]", this.getClass().getSimpleName(), this.request); | |
55 | + } | |
56 | +} | ... | ... |
src/client/java/com/mumfrey/webprefs/framework/WebPreferencesServiceTaskGet.java
0 → 100644
1 | +package com.mumfrey.webprefs.framework; | |
2 | + | |
3 | +import com.mumfrey.webprefs.interfaces.IWebPreferencesClient; | |
4 | +import com.mumfrey.webprefs.interfaces.IWebPreferencesProvider; | |
5 | +import com.mumfrey.webprefs.interfaces.IWebPreferencesRequest; | |
6 | +import com.mumfrey.webprefs.interfaces.IWebPreferencesResponse; | |
7 | + | |
8 | +class WebPreferencesServiceTaskGet extends WebPreferencesServiceTask | |
9 | +{ | |
10 | + WebPreferencesServiceTaskGet(IWebPreferencesProvider provider, IWebPreferencesClient client) | |
11 | + { | |
12 | + super(provider, client); | |
13 | + } | |
14 | + | |
15 | + @Override | |
16 | + public void onReceivedResponse(IWebPreferencesRequest request, IWebPreferencesResponse response) | |
17 | + { | |
18 | + IWebPreferencesClient client = this.getClient(); | |
19 | + if (client != null && response.hasValues()) | |
20 | + { | |
21 | + client.onGetRequestSuccess(response.getUUID(), response.getValues()); | |
22 | + } | |
23 | + } | |
24 | + | |
25 | + @Override | |
26 | + public void onRequestFailed(IWebPreferencesRequest request, Throwable th, RequestFailureReason reason) | |
27 | + { | |
28 | + IWebPreferencesClient client = this.getClient(); | |
29 | + if (client != null) | |
30 | + { | |
31 | + client.onGetRequestFailed(request.getUUID(), request.getKeys(), reason); | |
32 | + } | |
33 | + } | |
34 | +} | ... | ... |
src/client/java/com/mumfrey/webprefs/framework/WebPreferencesServiceTaskSet.java
0 → 100644
1 | +package com.mumfrey.webprefs.framework; | |
2 | + | |
3 | +import com.mumfrey.webprefs.interfaces.IWebPreferencesClient; | |
4 | +import com.mumfrey.webprefs.interfaces.IWebPreferencesProvider; | |
5 | +import com.mumfrey.webprefs.interfaces.IWebPreferencesRequest; | |
6 | +import com.mumfrey.webprefs.interfaces.IWebPreferencesResponse; | |
7 | + | |
8 | +class WebPreferencesServiceTaskSet extends WebPreferencesServiceTask | |
9 | +{ | |
10 | + WebPreferencesServiceTaskSet(IWebPreferencesProvider provider, IWebPreferencesClient client) | |
11 | + { | |
12 | + super(provider, client); | |
13 | + } | |
14 | + | |
15 | + @Override | |
16 | + public void onReceivedResponse(IWebPreferencesRequest request, IWebPreferencesResponse response) | |
17 | + { | |
18 | + IWebPreferencesClient client = this.getClient(); | |
19 | + if (client != null && response.hasSetters()) | |
20 | + { | |
21 | + client.onSetRequestSuccess(response.getUUID(), response.getSetters()); | |
22 | + } | |
23 | + } | |
24 | + | |
25 | + @Override | |
26 | + public void onRequestFailed(IWebPreferencesRequest request, Throwable th, RequestFailureReason reason) | |
27 | + { | |
28 | + IWebPreferencesClient client = this.getClient(); | |
29 | + if (client != null) | |
30 | + { | |
31 | + client.onSetRequestFailed(request.getUUID(), request.getKeys(), reason); | |
32 | + } | |
33 | + } | |
34 | +} | ... | ... |
src/client/java/com/mumfrey/webprefs/interfaces/IReliableWebPreferences.java
0 → 100644
1 | +package com.mumfrey.webprefs.interfaces; | |
2 | + | |
3 | +/** | |
4 | + * Reliable WebPreferences, no this isn't implemented yet | |
5 | + * | |
6 | + * @author Adam Mummery-Smith | |
7 | + */ | |
8 | +public interface IReliableWebPreferences extends IWebPreferences | |
9 | +{ | |
10 | + public void isSynchronised(String key); | |
11 | + | |
12 | + public void setWithNotify(String key, String value, IWebPreferencesListener listener); | |
13 | +} | ... | ... |
src/client/java/com/mumfrey/webprefs/interfaces/IWebPreferences.java
0 → 100644
1 | +package com.mumfrey.webprefs.interfaces; | |
2 | + | |
3 | +import java.util.Set; | |
4 | + | |
5 | +/** | |
6 | + * Web-based preferences, objects implementing this interface represent a remote | |
7 | + * asychronous Key/Value store which fetches and commits values on a best-effort | |
8 | + * basis. | |
9 | + * | |
10 | + * <p>Values in this store are not guaranteed to be correct, nor are values | |
11 | + * written to the store guaranteed to be successfully written to the store. | |
12 | + * | |
13 | + * <p>In general, consumers should call {@link #get} and {@link #set} with the | |
14 | + * assumption that reads and writes will happen asynchronously, eg. they should | |
15 | + * continually poll {@link #get} rather than caching any responses returned | |
16 | + * from the preferences object.</p> | |
17 | + * | |
18 | + * <p>Behaviour is such that any call to {@link #has} or {@link #get} will | |
19 | + * trigger asynchronous retrieval of properties from the backend server. | |
20 | + * Likewise calls to {@link #set} will trigger asychronous commit of dirty | |
21 | + * values (both reads and sets are batched and sent to the server as a single | |
22 | + * request where possible).</p> | |
23 | + * | |
24 | + * <p>Keys in the collection are restricted to lowercase letters and the period | |
25 | + * symbol only, failure to adhere to this format will cause any accessor methods | |
26 | + * to throw InvalidKeyException. Values in the collection are strings and are | |
27 | + * limited to 255 characters and must not be null, failure to adhere to these | |
28 | + * restrictions will cause accessors to throw {@link InvalidValueException}.</p> | |
29 | + * | |
30 | + * <p>Consumers can trigger synchronisation events on the collection using | |
31 | + * convenience methods, these methods do not ensure propagation of settings but | |
32 | + * act can be used to alter the normal behaviour of the set when necessary, for | |
33 | + * example to poll for updated values periodically. The methods request(), | |
34 | + * poll() and commit() can be used to trigger synchronisation events, consult | |
35 | + * the javadoc for each method for more details.</p> | |
36 | + * | |
37 | + * @author Adam Mummery-Smith | |
38 | + */ | |
39 | +public interface IWebPreferences | |
40 | +{ | |
41 | + /** | |
42 | + * Get the UUID assoicated with this preference collection | |
43 | + */ | |
44 | + public abstract String getUUID(); | |
45 | + | |
46 | + /** | |
47 | + * Get whether this collection is private or public | |
48 | + */ | |
49 | + public abstract boolean isPrivate(); | |
50 | + | |
51 | + /** | |
52 | + * Get whether this collection is read-only. Only the player's local | |
53 | + * preferences are writable. Any attempt to write to a read-only collection | |
54 | + * will throw a ReadOnlyPreferencesException. | |
55 | + */ | |
56 | + public abstract boolean isReadOnly(); | |
57 | + | |
58 | + /** | |
59 | + * Indicate to the set that the value specified by key is required an | |
60 | + * should be fetched from the server in the next batch. In general, | |
61 | + * consumers should use get() or has() to access the values in this | |
62 | + * collection, and expect that the collection will manage fetching the | |
63 | + * values from the server as part of the normal update cycle. However this | |
64 | + * method has two uses:</p> | |
65 | + * | |
66 | + * <ul> | |
67 | + * <li>You may choose to call {@link #request} when a handle to a | |
68 | + * preferences set is obtained, to indicate to the preferences set that | |
69 | + * you will require the value in the future and it should attempt to fetch | |
70 | + * the value so that it is ready for queries against the set later.</li> | |
71 | + * <li>You may wish to indicate to the set that it should request an | |
72 | + * updated value from the server for a key which it already has (normally | |
73 | + * values are cached indefinitely).</li> | |
74 | + * </ul> | |
75 | + * | |
76 | + * <p>In both cases, calls to request() should generally be limited to | |
77 | + * periodic request for values, to avoid constantly polling the server for | |
78 | + * values, and potentially running foul of request throttling at the server | |
79 | + * side</p> | |
80 | + * | |
81 | + * @param key key to request from the server | |
82 | + */ | |
83 | + public abstract void request(String key); | |
84 | + | |
85 | + /** | |
86 | + * Works exactly as {@link IWebPreferences#request(String)} but allows | |
87 | + * multiple values to be requested in a single invocation | |
88 | + * | |
89 | + * @param keys keys to request | |
90 | + */ | |
91 | + public abstract void request(String... keys); | |
92 | + | |
93 | + /** | |
94 | + * Works exactly as {@link IWebPreferences#request(String)} but allows | |
95 | + * multiple values to be requested in a single invocation | |
96 | + * | |
97 | + * @param keys keys to request | |
98 | + */ | |
99 | + public abstract void request(Set<String> keys); | |
100 | + | |
101 | + /** | |
102 | + * Calling <tt>poll()</tt> is essentially the same as calling | |
103 | + * {@link #request} for every key currently in the collection, this method | |
104 | + * can essentially be used to refresh the entire collection and should be | |
105 | + * called only in exceptional circumstances. It is called automatically by | |
106 | + * the preferences manager when connecting to a server. | |
107 | + * | |
108 | + * <p>Invoking <tt>poll()</tt> causes all of the values in the collection to | |
109 | + * be requested as a single batch</p> | |
110 | + */ | |
111 | + public abstract void poll(); | |
112 | + | |
113 | + /** | |
114 | + * Similar to {@link poll} except for property writes instead of property | |
115 | + * reads. Under normal circumstances it should not be necessary to call this | |
116 | + * method, since commits are handled asychronously by the update loop. | |
117 | + * However it can be used to forcibly commit even "clean" values to the | |
118 | + * server. This is useful if you wish to ensure that values on the server be | |
119 | + * set to match the current values in the collection, regardless of | |
120 | + * comodification elsewhere. | |
121 | + * | |
122 | + * @param force If set to false, only dirty properties are batched. If true, | |
123 | + * all properties are batched. | |
124 | + */ | |
125 | + public abstract void commit(boolean force); | |
126 | + | |
127 | + /** | |
128 | + * Get whether this collection has a value for the specified key, and | |
129 | + * triggers asnchronous retrieval if not | |
130 | + * | |
131 | + * @param key Key to check for | |
132 | + * @return | |
133 | + */ | |
134 | + public abstract boolean has(String key); | |
135 | + | |
136 | + /** | |
137 | + * Get the value for the specified key from this collection. Invoking this | |
138 | + * method causes asynchronous retrieval of the specified key if it is not | |
139 | + * found in the collection and returns null. If the key is found, then the | |
140 | + * value is returned and the collection remains unchanged. | |
141 | + * | |
142 | + * @param key | |
143 | + * @return | |
144 | + */ | |
145 | + public abstract String get(String key); | |
146 | + | |
147 | + /** | |
148 | + * Works exactly like get(String), including triggering asynchronous | |
149 | + * retrieval of the property if the property is not found in the collection, | |
150 | + * except that if the property is not found the specified defaultValue is | |
151 | + * returned instead of returning null. | |
152 | + * | |
153 | + * @param key | |
154 | + * @param defaultValue | |
155 | + * @return | |
156 | + */ | |
157 | + public abstract String get(String key, String defaultValue); | |
158 | + | |
159 | + /** | |
160 | + * Sets a value in the collection and marks it for asynchronous commit to | |
161 | + * the server. | |
162 | + * | |
163 | + * @param key | |
164 | + * @param value | |
165 | + */ | |
166 | + public abstract void set(String key, String value); | |
167 | + | |
168 | + /** | |
169 | + * Remove a key from this collection. Marks the key to be deleted from the | |
170 | + * server as well. | |
171 | + * | |
172 | + * @param key | |
173 | + */ | |
174 | + public abstract void remove(String key); | |
175 | +} | ... | ... |
src/client/java/com/mumfrey/webprefs/interfaces/IWebPreferencesClient.java
0 → 100644
1 | +package com.mumfrey.webprefs.interfaces; | |
2 | + | |
3 | +import java.util.Map; | |
4 | +import java.util.Set; | |
5 | + | |
6 | +import com.mumfrey.webprefs.framework.RequestFailureReason; | |
7 | + | |
8 | +public interface IWebPreferencesClient | |
9 | +{ | |
10 | + public abstract void onGetRequestSuccess(String uuid, Map<String, String> values); | |
11 | + | |
12 | + public abstract void onSetRequestSuccess(String uuid, Set<String> keys); | |
13 | + | |
14 | + public abstract void onGetRequestFailed(String uuid, Set<String> keys, RequestFailureReason reason); | |
15 | + | |
16 | + public abstract void onSetRequestFailed(String uuid, Set<String> keys, RequestFailureReason reason); | |
17 | +} | ... | ... |
src/client/java/com/mumfrey/webprefs/interfaces/IWebPreferencesListener.java
0 → 100644
1 | +package com.mumfrey.webprefs.interfaces; | |
2 | + | |
3 | +import java.util.Set; | |
4 | + | |
5 | +public interface IWebPreferencesListener | |
6 | +{ | |
7 | + public abstract void onCommitSuccess(IReliableWebPreferences preferences, Set<String> keys); | |
8 | + | |
9 | + public abstract void onCommitFailed(IReliableWebPreferences preferences, Set<String> keys); | |
10 | +} | ... | ... |
src/client/java/com/mumfrey/webprefs/interfaces/IWebPreferencesProvider.java
0 → 100644
1 | +package com.mumfrey.webprefs.interfaces; | |
2 | + | |
3 | +import java.util.Map; | |
4 | +import java.util.Set; | |
5 | + | |
6 | +import net.minecraft.util.Session; | |
7 | + | |
8 | +public interface IWebPreferencesProvider | |
9 | +{ | |
10 | + public abstract boolean isActive(); | |
11 | + | |
12 | + public abstract String getHostName(); | |
13 | + | |
14 | + public abstract Session getSession(); | |
15 | + | |
16 | + public abstract IWebPreferencesService getService(); | |
17 | + | |
18 | + public boolean requestGet(IWebPreferencesClient client, String uuid, Set<String> keys, boolean getPrivate); | |
19 | + | |
20 | + public boolean requestSet(IWebPreferencesClient client, String uuid, Map<String, String> values, boolean setPrivate); | |
21 | +} | ... | ... |
src/client/java/com/mumfrey/webprefs/interfaces/IWebPreferencesRequest.java
0 → 100644
1 | +package com.mumfrey.webprefs.interfaces; | |
2 | + | |
3 | +import java.io.Serializable; | |
4 | +import java.net.URI; | |
5 | +import java.util.Map; | |
6 | +import java.util.Set; | |
7 | + | |
8 | +public interface IWebPreferencesRequest extends Serializable | |
9 | +{ | |
10 | + public abstract IWebPreferencesServiceDelegate getDelegate(); | |
11 | + | |
12 | + public abstract boolean isValidationRequired(); | |
13 | + | |
14 | + public abstract URI getRequestURI(); | |
15 | + | |
16 | + public abstract String getUUID(); | |
17 | + | |
18 | + public abstract Set<String> getKeys(); | |
19 | + | |
20 | + public abstract Map<String, String> getPostVars(); | |
21 | + | |
22 | + public abstract void onReceivedResponse(IWebPreferencesResponse response); | |
23 | +} | ... | ... |
src/client/java/com/mumfrey/webprefs/interfaces/IWebPreferencesResponse.java
0 → 100644
1 | +package com.mumfrey.webprefs.interfaces; | |
2 | + | |
3 | +import java.io.Serializable; | |
4 | +import java.util.Map; | |
5 | +import java.util.Set; | |
6 | + | |
7 | +public interface IWebPreferencesResponse extends Serializable | |
8 | +{ | |
9 | + public abstract String getResponse(); | |
10 | + | |
11 | + public abstract String getMessage(); | |
12 | + | |
13 | + public abstract Throwable getThrowable(); | |
14 | + | |
15 | + public abstract String getUUID(); | |
16 | + | |
17 | + public abstract String getServerId(); | |
18 | + | |
19 | + public abstract boolean hasSetters(); | |
20 | + | |
21 | + public abstract Set<String> getSetters(); | |
22 | + | |
23 | + public abstract boolean hasValues(); | |
24 | + | |
25 | + public abstract Map<String, String> getValues(); | |
26 | +} | ... | ... |
src/client/java/com/mumfrey/webprefs/interfaces/IWebPreferencesService.java
0 → 100644
src/client/java/com/mumfrey/webprefs/interfaces/IWebPreferencesServiceDelegate.java
0 → 100644
1 | +package com.mumfrey.webprefs.interfaces; | |
2 | + | |
3 | +import net.minecraft.util.Session; | |
4 | + | |
5 | +import com.mumfrey.webprefs.framework.RequestFailureReason; | |
6 | + | |
7 | +public interface IWebPreferencesServiceDelegate | |
8 | +{ | |
9 | + public abstract String getHostName(); | |
10 | + | |
11 | + public abstract Session getSession(); | |
12 | + | |
13 | + public abstract void onReceivedResponse(IWebPreferencesRequest request, IWebPreferencesResponse response); | |
14 | + | |
15 | + public abstract void onRequestFailed(IWebPreferencesRequest request, Throwable th, RequestFailureReason reason); | |
16 | +} | ... | ... |
src/client/java/com/mumfrey/webprefs/interfaces/IWebPreferencesServiceMonitor.java
0 → 100644