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