Commit | Line | Data |
---|---|---|
e42573a0 | 1 | package be.nikiroo.fanfix.library; |
b0e88ebd NR |
2 | |
3 | import java.io.File; | |
4 | import java.io.IOException; | |
edf79e5e | 5 | import java.net.URL; |
e6249b0f | 6 | import java.net.UnknownHostException; |
68e2c6d2 NR |
7 | import java.util.ArrayList; |
8 | import java.util.List; | |
b0e88ebd | 9 | |
7e51d91c NR |
10 | import javax.net.ssl.SSLException; |
11 | ||
e42573a0 | 12 | import be.nikiroo.fanfix.Instance; |
b0e88ebd NR |
13 | import be.nikiroo.fanfix.data.MetaData; |
14 | import be.nikiroo.fanfix.data.Story; | |
16a81ef7 | 15 | import be.nikiroo.utils.Image; |
b0e88ebd NR |
16 | import be.nikiroo.utils.Progress; |
17 | import be.nikiroo.utils.Version; | |
62c63b07 | 18 | import be.nikiroo.utils.serial.server.ConnectActionClientObject; |
b0e88ebd | 19 | |
68e2c6d2 NR |
20 | /** |
21 | * This {@link BasicLibrary} will access a remote server to list the available | |
a85e8077 | 22 | * stories, and download the ones you try to load to the local directory |
68e2c6d2 NR |
23 | * specified in the configuration. |
24 | * | |
25 | * @author niki | |
26 | */ | |
27 | public class RemoteLibrary extends BasicLibrary { | |
b0e88ebd NR |
28 | private String host; |
29 | private int port; | |
ea734ab4 | 30 | private final String key; |
68e2c6d2 NR |
31 | |
32 | /** | |
33 | * Create a {@link RemoteLibrary} linked to the given server. | |
34 | * | |
2070ced5 NR |
35 | * @param key |
36 | * the key that will allow us to exchange information with the | |
37 | * server | |
68e2c6d2 NR |
38 | * @param host |
39 | * the host to contact or NULL for localhost | |
40 | * @param port | |
41 | * the port to contact it on | |
42 | */ | |
2070ced5 | 43 | public RemoteLibrary(String key, String host, int port) { |
ea734ab4 | 44 | this.key = key; |
b0e88ebd NR |
45 | this.host = host; |
46 | this.port = port; | |
b0e88ebd NR |
47 | } |
48 | ||
99ccbdf6 NR |
49 | @Override |
50 | public String getLibraryName() { | |
51 | return host + ":" + port; | |
52 | } | |
53 | ||
e6249b0f NR |
54 | @Override |
55 | public Status getStatus() { | |
56 | final Status[] result = new Status[1]; | |
57 | ||
58 | result[0] = Status.INVALID; | |
59 | ||
e6249b0f | 60 | try { |
41029926 | 61 | Instance.getTraceHandler().trace("Getting remote lib status..."); |
3040c4f0 | 62 | new ConnectActionClientObject(host, port, key) { |
e6249b0f NR |
63 | @Override |
64 | public void action(Version serverVersion) throws Exception { | |
7e51d91c | 65 | Object rep = send(new Object[] { "PING" }); |
ea734ab4 | 66 | |
7e51d91c NR |
67 | if ("PONG".equals(rep)) { |
68 | result[0] = Status.READY; | |
69 | } else { | |
e6249b0f NR |
70 | result[0] = Status.UNAUTORIZED; |
71 | } | |
72 | } | |
73 | ||
74 | @Override | |
75 | protected void onError(Exception e) { | |
7e51d91c NR |
76 | if (e instanceof SSLException) { |
77 | result[0] = Status.UNAUTORIZED; | |
78 | } else { | |
79 | result[0] = Status.UNAVAILABLE; | |
80 | } | |
e6249b0f | 81 | } |
ea734ab4 | 82 | }.connect(); |
e6249b0f NR |
83 | } catch (UnknownHostException e) { |
84 | result[0] = Status.INVALID; | |
85 | } catch (IllegalArgumentException e) { | |
86 | result[0] = Status.INVALID; | |
87 | } catch (Exception e) { | |
88 | result[0] = Status.UNAVAILABLE; | |
89 | } | |
90 | ||
41029926 | 91 | Instance.getTraceHandler().trace("Remote lib status: " + result[0]); |
e6249b0f NR |
92 | return result[0]; |
93 | } | |
94 | ||
b0e88ebd | 95 | @Override |
16a81ef7 NR |
96 | public Image getCover(final String luid) { |
97 | final Image[] result = new Image[1]; | |
b0e88ebd | 98 | |
b9ce9cad | 99 | try { |
3040c4f0 | 100 | new ConnectActionClientObject(host, port, key) { |
b9ce9cad NR |
101 | @Override |
102 | public void action(Version serverVersion) throws Exception { | |
3358927d | 103 | Object rep = send(new Object[] { "GET_COVER", luid }); |
16a81ef7 | 104 | result[0] = (Image) rep; |
b9ce9cad | 105 | } |
b0e88ebd | 106 | |
b9ce9cad NR |
107 | @Override |
108 | protected void onError(Exception e) { | |
7e51d91c NR |
109 | if (e instanceof SSLException) { |
110 | Instance.getTraceHandler().error( | |
111 | "Connection refused (bad key)"); | |
112 | } else { | |
113 | Instance.getTraceHandler().error(e); | |
114 | } | |
b9ce9cad NR |
115 | } |
116 | }.connect(); | |
117 | } catch (Exception e) { | |
118 | Instance.getTraceHandler().error(e); | |
119 | } | |
b0e88ebd | 120 | |
b9ce9cad | 121 | return result[0]; |
085a2f9a NR |
122 | } |
123 | ||
124 | @Override | |
e1de8087 | 125 | public Image getCustomSourceCover(final String source) { |
3989dfc5 NR |
126 | return getCustomCover(source, "SOURCE"); |
127 | } | |
128 | ||
129 | @Override | |
130 | public Image getCustomAuthorCover(final String author) { | |
131 | return getCustomCover(author, "AUTHOR"); | |
132 | } | |
133 | ||
134 | // type: "SOURCE" or "AUTHOR" | |
135 | private Image getCustomCover(final String source, final String type) { | |
16a81ef7 | 136 | final Image[] result = new Image[1]; |
b9ce9cad NR |
137 | |
138 | try { | |
3040c4f0 | 139 | new ConnectActionClientObject(host, port, key) { |
b9ce9cad NR |
140 | @Override |
141 | public void action(Version serverVersion) throws Exception { | |
3358927d NR |
142 | Object rep = send(new Object[] { "GET_CUSTOM_COVER", type, |
143 | source }); | |
16a81ef7 | 144 | result[0] = (Image) rep; |
b9ce9cad NR |
145 | } |
146 | ||
147 | @Override | |
148 | protected void onError(Exception e) { | |
7e51d91c NR |
149 | if (e instanceof SSLException) { |
150 | Instance.getTraceHandler().error( | |
151 | "Connection refused (bad key)"); | |
152 | } else { | |
153 | Instance.getTraceHandler().error(e); | |
154 | } | |
b9ce9cad NR |
155 | } |
156 | }.connect(); | |
157 | } catch (Exception e) { | |
158 | Instance.getTraceHandler().error(e); | |
159 | } | |
160 | ||
161 | return result[0]; | |
b0e88ebd | 162 | } |
68e2c6d2 NR |
163 | |
164 | @Override | |
ff05b828 | 165 | public synchronized Story getStory(final String luid, Progress pg) { |
b9ce9cad NR |
166 | final Progress pgF = pg; |
167 | final Story[] result = new Story[1]; | |
68e2c6d2 | 168 | |
b9ce9cad | 169 | try { |
3040c4f0 | 170 | new ConnectActionClientObject(host, port, key) { |
b9ce9cad NR |
171 | @Override |
172 | public void action(Version serverVersion) throws Exception { | |
173 | Progress pg = pgF; | |
174 | if (pg == null) { | |
175 | pg = new Progress(); | |
176 | } | |
177 | ||
3358927d | 178 | Object rep = send(new Object[] { "GET_STORY", luid }); |
b9ce9cad NR |
179 | |
180 | MetaData meta = null; | |
181 | if (rep instanceof MetaData) { | |
182 | meta = (MetaData) rep; | |
183 | if (meta.getWords() <= Integer.MAX_VALUE) { | |
184 | pg.setMinMax(0, (int) meta.getWords()); | |
185 | } | |
186 | } | |
187 | ||
188 | List<Object> list = new ArrayList<Object>(); | |
27eba894 | 189 | for (Object obj = send(null); obj != null; obj = send(null)) { |
b9ce9cad NR |
190 | list.add(obj); |
191 | pg.add(1); | |
192 | } | |
193 | ||
194 | result[0] = RemoteLibraryServer.rebuildStory(list); | |
195 | pg.done(); | |
196 | } | |
197 | ||
198 | @Override | |
199 | protected void onError(Exception e) { | |
7e51d91c NR |
200 | if (e instanceof SSLException) { |
201 | Instance.getTraceHandler().error( | |
202 | "Connection refused (bad key)"); | |
203 | } else { | |
204 | Instance.getTraceHandler().error(e); | |
205 | } | |
b9ce9cad NR |
206 | } |
207 | }.connect(); | |
208 | } catch (Exception e) { | |
209 | Instance.getTraceHandler().error(e); | |
210 | } | |
211 | ||
212 | return result[0]; | |
68e2c6d2 NR |
213 | } |
214 | ||
215 | @Override | |
b9ce9cad NR |
216 | public synchronized Story save(final Story story, final String luid, |
217 | Progress pg) throws IOException { | |
0fa0fe95 NR |
218 | final String[] luidSaved = new String[1]; |
219 | Progress pgSave = new Progress(); | |
220 | Progress pgRefresh = new Progress(); | |
221 | if (pg == null) { | |
222 | pg = new Progress(); | |
223 | } | |
224 | ||
225 | pg.setMinMax(0, 10); | |
226 | pg.addProgress(pgSave, 9); | |
227 | pg.addProgress(pgRefresh, 1); | |
228 | ||
229 | final Progress pgF = pgSave; | |
b9ce9cad | 230 | |
3040c4f0 | 231 | new ConnectActionClientObject(host, port, key) { |
b9ce9cad NR |
232 | @Override |
233 | public void action(Version serverVersion) throws Exception { | |
234 | Progress pg = pgF; | |
b9ce9cad NR |
235 | if (story.getMeta().getWords() <= Integer.MAX_VALUE) { |
236 | pg.setMinMax(0, (int) story.getMeta().getWords()); | |
237 | } | |
238 | ||
3358927d | 239 | send(new Object[] { "SAVE_STORY", luid }); |
b9ce9cad NR |
240 | |
241 | List<Object> list = RemoteLibraryServer.breakStory(story); | |
242 | for (Object obj : list) { | |
243 | send(obj); | |
244 | pg.add(1); | |
245 | } | |
246 | ||
edf79e5e | 247 | luidSaved[0] = (String) send(null); |
0fa0fe95 | 248 | |
b9ce9cad NR |
249 | pg.done(); |
250 | } | |
251 | ||
252 | @Override | |
253 | protected void onError(Exception e) { | |
7e51d91c NR |
254 | if (e instanceof SSLException) { |
255 | Instance.getTraceHandler().error( | |
256 | "Connection refused (bad key)"); | |
257 | } else { | |
258 | Instance.getTraceHandler().error(e); | |
259 | } | |
b9ce9cad NR |
260 | } |
261 | }.connect(); | |
085a2f9a NR |
262 | |
263 | // because the meta changed: | |
edf79e5e | 264 | MetaData meta = getInfo(luidSaved[0]); |
efa3c511 NR |
265 | if (story.getMeta().getClass() != null) { |
266 | // If already available locally: | |
267 | meta.setCover(story.getMeta().getCover()); | |
268 | } else { | |
269 | // If required: | |
270 | meta.setCover(getCover(meta.getLuid())); | |
271 | } | |
edf79e5e | 272 | story.setMeta(meta); |
0fa0fe95 NR |
273 | |
274 | pg.done(); | |
085a2f9a NR |
275 | |
276 | return story; | |
68e2c6d2 NR |
277 | } |
278 | ||
279 | @Override | |
b9ce9cad | 280 | public synchronized void delete(final String luid) throws IOException { |
3040c4f0 | 281 | new ConnectActionClientObject(host, port, key) { |
b9ce9cad NR |
282 | @Override |
283 | public void action(Version serverVersion) throws Exception { | |
3358927d | 284 | send(new Object[] { "DELETE_STORY", luid }); |
b9ce9cad NR |
285 | } |
286 | ||
287 | @Override | |
288 | protected void onError(Exception e) { | |
7e51d91c NR |
289 | if (e instanceof SSLException) { |
290 | Instance.getTraceHandler().error( | |
291 | "Connection refused (bad key)"); | |
292 | } else { | |
293 | Instance.getTraceHandler().error(e); | |
294 | } | |
b9ce9cad NR |
295 | } |
296 | }.connect(); | |
68e2c6d2 NR |
297 | } |
298 | ||
299 | @Override | |
b9ce9cad | 300 | public void setSourceCover(final String source, final String luid) { |
3989dfc5 NR |
301 | setCover(source, luid, "SOURCE"); |
302 | } | |
303 | ||
304 | @Override | |
305 | public void setAuthorCover(final String author, final String luid) { | |
306 | setCover(author, luid, "AUTHOR"); | |
307 | } | |
308 | ||
309 | // type = "SOURCE" | "AUTHOR" | |
310 | private void setCover(final String value, final String luid, | |
311 | final String type) { | |
b9ce9cad | 312 | try { |
3040c4f0 | 313 | new ConnectActionClientObject(host, port, key) { |
b9ce9cad NR |
314 | @Override |
315 | public void action(Version serverVersion) throws Exception { | |
3358927d | 316 | send(new Object[] { "SET_COVER", type, value, luid }); |
b9ce9cad NR |
317 | } |
318 | ||
319 | @Override | |
320 | protected void onError(Exception e) { | |
7e51d91c NR |
321 | if (e instanceof SSLException) { |
322 | Instance.getTraceHandler().error( | |
323 | "Connection refused (bad key)"); | |
324 | } else { | |
325 | Instance.getTraceHandler().error(e); | |
326 | } | |
b9ce9cad NR |
327 | } |
328 | }.connect(); | |
329 | } catch (IOException e) { | |
330 | Instance.getTraceHandler().error(e); | |
edf79e5e NR |
331 | } |
332 | } | |
333 | ||
334 | @Override | |
335 | // Could work (more slowly) without it | |
336 | public Story imprt(final URL url, Progress pg) throws IOException { | |
00f6344a NR |
337 | // Import the file locally if it is actually a file |
338 | if (url == null || url.getProtocol().equalsIgnoreCase("file")) { | |
339 | return super.imprt(url, pg); | |
340 | } | |
341 | ||
342 | // Import it remotely if it is an URL | |
343 | ||
edf79e5e NR |
344 | if (pg == null) { |
345 | pg = new Progress(); | |
346 | } | |
347 | ||
348 | pg.setMinMax(0, 2); | |
349 | Progress pgImprt = new Progress(); | |
350 | Progress pgGet = new Progress(); | |
351 | pg.addProgress(pgImprt, 1); | |
352 | pg.addProgress(pgGet, 1); | |
353 | ||
354 | final Progress pgF = pgImprt; | |
355 | final String[] luid = new String[1]; | |
356 | ||
357 | try { | |
3040c4f0 | 358 | new ConnectActionClientObject(host, port, key) { |
edf79e5e NR |
359 | @Override |
360 | public void action(Version serverVersion) throws Exception { | |
361 | Progress pg = pgF; | |
362 | ||
3358927d | 363 | Object rep = send(new Object[] { "IMPORT", url.toString() }); |
edf79e5e NR |
364 | |
365 | while (true) { | |
366 | if (!RemoteLibraryServer.updateProgress(pg, rep)) { | |
367 | break; | |
368 | } | |
369 | ||
370 | rep = send(null); | |
371 | } | |
372 | ||
373 | pg.done(); | |
374 | luid[0] = (String) rep; | |
375 | } | |
376 | ||
377 | @Override | |
378 | protected void onError(Exception e) { | |
7e51d91c NR |
379 | if (e instanceof SSLException) { |
380 | Instance.getTraceHandler().error( | |
381 | "Connection refused (bad key)"); | |
382 | } else { | |
383 | Instance.getTraceHandler().error(e); | |
384 | } | |
edf79e5e NR |
385 | } |
386 | }.connect(); | |
387 | } catch (IOException e) { | |
388 | Instance.getTraceHandler().error(e); | |
389 | } | |
390 | ||
391 | if (luid[0] == null) { | |
392 | throw new IOException("Remote failure"); | |
393 | } | |
394 | ||
395 | Story story = getStory(luid[0], pgGet); | |
396 | pgGet.done(); | |
397 | ||
398 | pg.done(); | |
399 | return story; | |
400 | } | |
401 | ||
402 | @Override | |
403 | // Could work (more slowly) without it | |
c8d48938 NR |
404 | protected synchronized void changeSTA(final String luid, |
405 | final String newSource, final String newTitle, | |
406 | final String newAuthor, Progress pg) throws IOException { | |
edf79e5e NR |
407 | final Progress pgF = pg == null ? new Progress() : pg; |
408 | ||
409 | try { | |
3040c4f0 | 410 | new ConnectActionClientObject(host, port, key) { |
edf79e5e NR |
411 | @Override |
412 | public void action(Version serverVersion) throws Exception { | |
413 | Progress pg = pgF; | |
414 | ||
3358927d NR |
415 | Object rep = send(new Object[] { "CHANGE_STA", luid, |
416 | newSource, newTitle, newAuthor }); | |
edf79e5e NR |
417 | while (true) { |
418 | if (!RemoteLibraryServer.updateProgress(pg, rep)) { | |
419 | break; | |
420 | } | |
421 | ||
422 | rep = send(null); | |
423 | } | |
424 | } | |
425 | ||
426 | @Override | |
427 | protected void onError(Exception e) { | |
7e51d91c NR |
428 | if (e instanceof SSLException) { |
429 | Instance.getTraceHandler().error( | |
430 | "Connection refused (bad key)"); | |
431 | } else { | |
432 | Instance.getTraceHandler().error(e); | |
433 | } | |
edf79e5e NR |
434 | } |
435 | }.connect(); | |
436 | } catch (IOException e) { | |
437 | Instance.getTraceHandler().error(e); | |
b9ce9cad | 438 | } |
68e2c6d2 NR |
439 | } |
440 | ||
ff05b828 | 441 | @Override |
2249988a | 442 | public synchronized File getFile(final String luid, Progress pg) { |
ff05b828 NR |
443 | throw new java.lang.InternalError( |
444 | "Operation not supportorted on remote Libraries"); | |
445 | } | |
446 | ||
468b960b NR |
447 | /** |
448 | * Stop the server. | |
449 | */ | |
450 | public void exit() { | |
451 | try { | |
3040c4f0 | 452 | new ConnectActionClientObject(host, port, key) { |
468b960b NR |
453 | @Override |
454 | public void action(Version serverVersion) throws Exception { | |
3358927d | 455 | send(new Object[] { "EXIT" }); |
468b960b NR |
456 | } |
457 | ||
458 | @Override | |
459 | protected void onError(Exception e) { | |
7e51d91c NR |
460 | if (e instanceof SSLException) { |
461 | Instance.getTraceHandler().error( | |
462 | "Connection refused (bad key)"); | |
463 | } else { | |
464 | Instance.getTraceHandler().error(e); | |
465 | } | |
468b960b NR |
466 | } |
467 | }.connect(); | |
468 | } catch (IOException e) { | |
469 | Instance.getTraceHandler().error(e); | |
470 | } | |
471 | } | |
472 | ||
e272f05f NR |
473 | @Override |
474 | public synchronized MetaData getInfo(String luid) { | |
475 | List<MetaData> metas = getMetasList(luid, null); | |
476 | if (!metas.isEmpty()) { | |
477 | return metas.get(0); | |
478 | } | |
479 | ||
480 | return null; | |
481 | } | |
482 | ||
14b57448 | 483 | @Override |
b9ce9cad | 484 | protected List<MetaData> getMetas(Progress pg) { |
e272f05f NR |
485 | return getMetasList("*", pg); |
486 | } | |
487 | ||
488 | @Override | |
efa3c511 NR |
489 | protected void updateInfo(MetaData meta) { |
490 | // Will be taken care of directly server side | |
491 | } | |
492 | ||
493 | @Override | |
c8d48938 | 494 | protected void invalidateInfo(String luid) { |
efa3c511 | 495 | // Will be taken care of directly server side |
e272f05f NR |
496 | } |
497 | ||
498 | // The following methods are only used by Save and Delete in BasicLibrary: | |
499 | ||
500 | @Override | |
501 | protected int getNextId() { | |
502 | throw new java.lang.InternalError("Should not have been called"); | |
503 | } | |
504 | ||
505 | @Override | |
506 | protected void doDelete(String luid) throws IOException { | |
507 | throw new java.lang.InternalError("Should not have been called"); | |
508 | } | |
509 | ||
510 | @Override | |
511 | protected Story doSave(Story story, Progress pg) throws IOException { | |
512 | throw new java.lang.InternalError("Should not have been called"); | |
513 | } | |
514 | ||
515 | // | |
516 | ||
517 | /** | |
518 | * Return the meta of the given story or a list of all known metas if the | |
519 | * luid is "*". | |
9f51d8ab NR |
520 | * <p> |
521 | * Will not get the covers. | |
e272f05f NR |
522 | * |
523 | * @param luid | |
524 | * the luid of the story or * | |
525 | * @param pg | |
526 | * the optional progress | |
527 | * | |
528 | * | |
529 | * @return the metas | |
530 | */ | |
531 | private List<MetaData> getMetasList(final String luid, Progress pg) { | |
b9ce9cad NR |
532 | final Progress pgF = pg; |
533 | final List<MetaData> metas = new ArrayList<MetaData>(); | |
74a40dfb | 534 | |
ff05b828 | 535 | try { |
3040c4f0 | 536 | new ConnectActionClientObject(host, port, key) { |
ff05b828 NR |
537 | @Override |
538 | public void action(Version serverVersion) throws Exception { | |
b9ce9cad NR |
539 | Progress pg = pgF; |
540 | if (pg == null) { | |
541 | pg = new Progress(); | |
851dd538 | 542 | } |
74a40dfb | 543 | |
3358927d | 544 | Object rep = send(new Object[] { "GET_METADATA", luid }); |
74a40dfb | 545 | |
b9ce9cad NR |
546 | while (true) { |
547 | if (!RemoteLibraryServer.updateProgress(pg, rep)) { | |
548 | break; | |
549 | } | |
74a40dfb | 550 | |
b9ce9cad | 551 | rep = send(null); |
ff05b828 | 552 | } |
851dd538 | 553 | |
e272f05f NR |
554 | if (rep instanceof MetaData[]) { |
555 | for (MetaData meta : (MetaData[]) rep) { | |
556 | metas.add(meta); | |
557 | } | |
558 | } else if (rep != null) { | |
559 | metas.add((MetaData) rep); | |
b9ce9cad | 560 | } |
851dd538 NR |
561 | } |
562 | ||
563 | @Override | |
564 | protected void onError(Exception e) { | |
7e51d91c NR |
565 | if (e instanceof SSLException) { |
566 | Instance.getTraceHandler().error( | |
567 | "Connection refused (bad key)"); | |
568 | } else { | |
569 | Instance.getTraceHandler().error(e); | |
570 | } | |
ff05b828 NR |
571 | } |
572 | }.connect(); | |
ff05b828 | 573 | } catch (Exception e) { |
62c63b07 | 574 | Instance.getTraceHandler().error(e); |
ff05b828 | 575 | } |
b9ce9cad NR |
576 | |
577 | return metas; | |
578 | } | |
b0e88ebd | 579 | } |