c2ed310b5da0fbf9f1e49dc57eacbb17c448e3fe
[fanfix.git] / src / be / nikiroo / fanfix / library / RemoteLibrary.java
1 package be.nikiroo.fanfix.library;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.net.URL;
6 import java.net.UnknownHostException;
7 import java.util.ArrayList;
8 import java.util.List;
9
10 import javax.net.ssl.SSLException;
11
12 import be.nikiroo.fanfix.Instance;
13 import be.nikiroo.fanfix.data.MetaData;
14 import be.nikiroo.fanfix.data.Story;
15 import be.nikiroo.utils.Image;
16 import be.nikiroo.utils.Progress;
17 import be.nikiroo.utils.Version;
18 import be.nikiroo.utils.serial.server.ConnectActionClientObject;
19
20 /**
21 * This {@link BasicLibrary} will access a remote server to list the available
22 * stories, and download the ones you try to load to the local directory
23 * specified in the configuration.
24 *
25 * @author niki
26 */
27 public class RemoteLibrary extends BasicLibrary {
28 private String host;
29 private int port;
30 private final String key;
31
32 /**
33 * Create a {@link RemoteLibrary} linked to the given server.
34 *
35 * @param key
36 * the key that will allow us to exchange information with the
37 * server
38 * @param host
39 * the host to contact or NULL for localhost
40 * @param port
41 * the port to contact it on
42 */
43 public RemoteLibrary(String key, String host, int port) {
44 this.key = key;
45 this.host = host;
46 this.port = port;
47 }
48
49 @Override
50 public String getLibraryName() {
51 return host + ":" + port;
52 }
53
54 @Override
55 public Status getStatus() {
56 final Status[] result = new Status[1];
57
58 result[0] = Status.INVALID;
59
60 try {
61 Instance.getTraceHandler().trace("Getting remote lib status...");
62 new ConnectActionClientObject(host, port, key) {
63 @Override
64 public void action(Version serverVersion) throws Exception {
65 Object rep = send(new Object[] { "PING" });
66
67 if ("PONG".equals(rep)) {
68 result[0] = Status.READY;
69 } else {
70 result[0] = Status.UNAUTORIZED;
71 }
72 }
73
74 @Override
75 protected void onError(Exception e) {
76 if (e instanceof SSLException) {
77 result[0] = Status.UNAUTORIZED;
78 } else {
79 result[0] = Status.UNAVAILABLE;
80 }
81 }
82 }.connect();
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
91 Instance.getTraceHandler().trace("Remote lib status: " + result[0]);
92 return result[0];
93 }
94
95 @Override
96 public Image getCover(final String luid) {
97 final Image[] result = new Image[1];
98
99 try {
100 new ConnectActionClientObject(host, port, key) {
101 @Override
102 public void action(Version serverVersion) throws Exception {
103 Object rep = send(new Object[] { "GET_COVER", luid });
104 result[0] = (Image) rep;
105 }
106
107 @Override
108 protected void onError(Exception e) {
109 if (e instanceof SSLException) {
110 Instance.getTraceHandler().error(
111 "Connection refused (bad key)");
112 } else {
113 Instance.getTraceHandler().error(e);
114 }
115 }
116 }.connect();
117 } catch (Exception e) {
118 Instance.getTraceHandler().error(e);
119 }
120
121 return result[0];
122 }
123
124 @Override
125 public Image getCustomSourceCover(final String source) {
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) {
136 final Image[] result = new Image[1];
137
138 try {
139 new ConnectActionClientObject(host, port, key) {
140 @Override
141 public void action(Version serverVersion) throws Exception {
142 Object rep = send(new Object[] { "GET_CUSTOM_COVER", type,
143 source });
144 result[0] = (Image) rep;
145 }
146
147 @Override
148 protected void onError(Exception e) {
149 if (e instanceof SSLException) {
150 Instance.getTraceHandler().error(
151 "Connection refused (bad key)");
152 } else {
153 Instance.getTraceHandler().error(e);
154 }
155 }
156 }.connect();
157 } catch (Exception e) {
158 Instance.getTraceHandler().error(e);
159 }
160
161 return result[0];
162 }
163
164 @Override
165 public synchronized Story getStory(final String luid, Progress pg) {
166 final Progress pgF = pg;
167 final Story[] result = new Story[1];
168
169 try {
170 new ConnectActionClientObject(host, port, key) {
171 @Override
172 public void action(Version serverVersion) throws Exception {
173 Progress pg = pgF;
174 if (pg == null) {
175 pg = new Progress();
176 }
177
178 Object rep = send(new Object[] { "GET_STORY", luid });
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>();
189 for (Object obj = send(null); obj != null; obj = send(null)) {
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) {
200 if (e instanceof SSLException) {
201 Instance.getTraceHandler().error(
202 "Connection refused (bad key)");
203 } else {
204 Instance.getTraceHandler().error(e);
205 }
206 }
207 }.connect();
208 } catch (Exception e) {
209 Instance.getTraceHandler().error(e);
210 }
211
212 return result[0];
213 }
214
215 @Override
216 public synchronized Story save(final Story story, final String luid,
217 Progress pg) throws IOException {
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;
230
231 new ConnectActionClientObject(host, port, key) {
232 @Override
233 public void action(Version serverVersion) throws Exception {
234 Progress pg = pgF;
235 if (story.getMeta().getWords() <= Integer.MAX_VALUE) {
236 pg.setMinMax(0, (int) story.getMeta().getWords());
237 }
238
239 send(new Object[] { "SAVE_STORY", luid });
240
241 List<Object> list = RemoteLibraryServer.breakStory(story);
242 for (Object obj : list) {
243 send(obj);
244 pg.add(1);
245 }
246
247 luidSaved[0] = (String) send(null);
248
249 pg.done();
250 }
251
252 @Override
253 protected void onError(Exception e) {
254 if (e instanceof SSLException) {
255 Instance.getTraceHandler().error(
256 "Connection refused (bad key)");
257 } else {
258 Instance.getTraceHandler().error(e);
259 }
260 }
261 }.connect();
262
263 // because the meta changed:
264 MetaData meta = getInfo(luidSaved[0]);
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 }
272 story.setMeta(meta);
273
274 pg.done();
275
276 return story;
277 }
278
279 @Override
280 public synchronized void delete(final String luid) throws IOException {
281 new ConnectActionClientObject(host, port, key) {
282 @Override
283 public void action(Version serverVersion) throws Exception {
284 send(new Object[] { "DELETE_STORY", luid });
285 }
286
287 @Override
288 protected void onError(Exception e) {
289 if (e instanceof SSLException) {
290 Instance.getTraceHandler().error(
291 "Connection refused (bad key)");
292 } else {
293 Instance.getTraceHandler().error(e);
294 }
295 }
296 }.connect();
297 }
298
299 @Override
300 public void setSourceCover(final String source, final String luid) {
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) {
312 try {
313 new ConnectActionClientObject(host, port, key) {
314 @Override
315 public void action(Version serverVersion) throws Exception {
316 send(new Object[] { "SET_COVER", type, value, luid });
317 }
318
319 @Override
320 protected void onError(Exception e) {
321 if (e instanceof SSLException) {
322 Instance.getTraceHandler().error(
323 "Connection refused (bad key)");
324 } else {
325 Instance.getTraceHandler().error(e);
326 }
327 }
328 }.connect();
329 } catch (IOException e) {
330 Instance.getTraceHandler().error(e);
331 }
332 }
333
334 @Override
335 // Could work (more slowly) without it
336 public Story imprt(final URL url, Progress pg) throws IOException {
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
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 {
358 new ConnectActionClientObject(host, port, key) {
359 @Override
360 public void action(Version serverVersion) throws Exception {
361 Progress pg = pgF;
362
363 Object rep = send(new Object[] { "IMPORT", url.toString() });
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) {
379 if (e instanceof SSLException) {
380 Instance.getTraceHandler().error(
381 "Connection refused (bad key)");
382 } else {
383 Instance.getTraceHandler().error(e);
384 }
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
404 protected synchronized void changeSTA(final String luid,
405 final String newSource, final String newTitle,
406 final String newAuthor, Progress pg) throws IOException {
407 final Progress pgF = pg == null ? new Progress() : pg;
408
409 try {
410 new ConnectActionClientObject(host, port, key) {
411 @Override
412 public void action(Version serverVersion) throws Exception {
413 Progress pg = pgF;
414
415 Object rep = send(new Object[] { "CHANGE_STA", luid,
416 newSource, newTitle, newAuthor });
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) {
428 if (e instanceof SSLException) {
429 Instance.getTraceHandler().error(
430 "Connection refused (bad key)");
431 } else {
432 Instance.getTraceHandler().error(e);
433 }
434 }
435 }.connect();
436 } catch (IOException e) {
437 Instance.getTraceHandler().error(e);
438 }
439 }
440
441 @Override
442 public synchronized File getFile(final String luid, Progress pg) {
443 throw new java.lang.InternalError(
444 "Operation not supportorted on remote Libraries");
445 }
446
447 /**
448 * Stop the server.
449 */
450 public void exit() {
451 try {
452 new ConnectActionClientObject(host, port, key) {
453 @Override
454 public void action(Version serverVersion) throws Exception {
455 send(new Object[] { "EXIT" });
456 }
457
458 @Override
459 protected void onError(Exception e) {
460 if (e instanceof SSLException) {
461 Instance.getTraceHandler().error(
462 "Connection refused (bad key)");
463 } else {
464 Instance.getTraceHandler().error(e);
465 }
466 }
467 }.connect();
468 } catch (IOException e) {
469 Instance.getTraceHandler().error(e);
470 }
471 }
472
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
483 @Override
484 protected List<MetaData> getMetas(Progress pg) {
485 return getMetasList("*", pg);
486 }
487
488 @Override
489 protected void updateInfo(MetaData meta) {
490 // Will be taken care of directly server side
491 }
492
493 @Override
494 protected void invalidateInfo(String luid) {
495 // Will be taken care of directly server side
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 "*".
520 * <p>
521 * Will not get the covers.
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) {
532 final Progress pgF = pg;
533 final List<MetaData> metas = new ArrayList<MetaData>();
534
535 try {
536 new ConnectActionClientObject(host, port, key) {
537 @Override
538 public void action(Version serverVersion) throws Exception {
539 Progress pg = pgF;
540 if (pg == null) {
541 pg = new Progress();
542 }
543
544 Object rep = send(new Object[] { "GET_METADATA", luid });
545
546 while (true) {
547 if (!RemoteLibraryServer.updateProgress(pg, rep)) {
548 break;
549 }
550
551 rep = send(null);
552 }
553
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);
560 }
561 }
562
563 @Override
564 protected void onError(Exception e) {
565 if (e instanceof SSLException) {
566 Instance.getTraceHandler().error(
567 "Connection refused (bad key)");
568 } else {
569 Instance.getTraceHandler().error(e);
570 }
571 }
572 }.connect();
573 } catch (Exception e) {
574 Instance.getTraceHandler().error(e);
575 }
576
577 return metas;
578 }
579 }