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