1 package be
.nikiroo
.fanfix
.library
;
4 import java
.io
.IOException
;
6 import java
.net
.UnknownHostException
;
7 import java
.util
.ArrayList
;
10 import javax
.net
.ssl
.SSLException
;
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
;
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.
27 public class RemoteLibrary
extends BasicLibrary
{
30 private final String key
;
31 private final String subkey
;
33 // informative only (server will make the actual checks)
36 // TODO: error handling is not up to par!
39 * Create a {@link RemoteLibrary} linked to the given server.
41 * Note that the key is structured:
42 * <tt><b><i>xxx</i></b>(|<b><i>yyy</i></b>|<b>wl</b>)(|<b>rw</b>)</tt>
44 * Note that anything before the first pipe (<tt>|</tt>) character is
45 * considered to be the encryption key, anything after that character is
46 * called the subkey (including the other pipe characters and flags!).
48 * This is important because the subkey (including the pipe characters and
49 * flags) must be present as-is in the server configuration file to be
52 * <li><b><i>xxx</i></b>: the encryption key used to communicate with the
54 * <li><b><i>yyy</i></b>: the secondary key</li>
55 * <li><b>rw</b>: flag to allow read and write access if it is not the
56 * default on this server</li>
57 * <li><b>wl</b>: flag to allow access to all the stories (bypassing the
58 * whitelist if it exists)</li>
63 * <li><b>my_key</b>: normal connection, will take the default server
65 * <li><b>my_key|agzyzz|wl</b>: will ask to bypass the white list (if it
67 * <li><b>my_key|agzyzz|rw</b>: will ask read-write access (if the default
69 * <li><b>my_key|agzyzz|wl|rw</b>: will ask both read-write access and white
74 * the key that will allow us to exchange information with the
77 * the host to contact or NULL for localhost
79 * the port to contact it on
81 public RemoteLibrary(String key
, String host
, int port
) {
84 index
= key
.indexOf('|');
88 this.key
= key
.substring(0, index
);
89 this.subkey
= key
.substring(index
+ 1);
100 public String
getLibraryName() {
101 return host
+ ":" + port
;
105 public Status
getStatus() {
106 Instance
.getTraceHandler().trace("Getting remote lib status...");
107 Status status
= getStatusDo();
108 Instance
.getTraceHandler().trace("Remote lib status: " + status
);
112 private boolean check() {
113 Status status
= getStatusDo();
114 if (status
!= Status
.READY
) {
115 Instance
.getTraceHandler().error("Remote lib not ready: " + status
);
122 private Status
getStatusDo() {
123 final Status
[] result
= new Status
[1];
125 result
[0] = Status
.INVALID
;
128 new ConnectActionClientObject(host
, port
, key
) {
130 public void action(Version serverVersion
) throws Exception
{
131 Object rep
= send(new Object
[] { subkey
, "PING" });
133 if ("r/w".equals(rep
)) {
135 result
[0] = Status
.READY
;
136 } else if ("r/o".equals(rep
)) {
138 result
[0] = Status
.READY
;
140 result
[0] = Status
.UNAUTHORIZED
;
145 protected void onError(Exception e
) {
146 if (e
instanceof SSLException
) {
147 result
[0] = Status
.UNAUTHORIZED
;
149 result
[0] = Status
.UNAVAILABLE
;
153 } catch (UnknownHostException e
) {
154 result
[0] = Status
.INVALID
;
155 } catch (IllegalArgumentException e
) {
156 result
[0] = Status
.INVALID
;
157 } catch (Exception e
) {
158 result
[0] = Status
.UNAVAILABLE
;
165 public Image
getCover(final String luid
) {
170 final Image
[] result
= new Image
[1];
173 new ConnectActionClientObject(host
, port
, key
) {
175 public void action(Version serverVersion
) throws Exception
{
176 Object rep
= send(new Object
[] { subkey
, "GET_COVER", luid
});
177 result
[0] = (Image
) rep
;
181 protected void onError(Exception e
) {
182 if (e
instanceof SSLException
) {
183 Instance
.getTraceHandler().error(
184 "Connection refused (bad key)");
186 Instance
.getTraceHandler().error(e
);
190 } catch (Exception e
) {
191 Instance
.getTraceHandler().error(e
);
198 public Image
getCustomSourceCover(final String source
) {
203 return getCustomCover(source
, "SOURCE");
207 public Image
getCustomAuthorCover(final String author
) {
212 return getCustomCover(author
, "AUTHOR");
215 // type: "SOURCE" or "AUTHOR"
216 private Image
getCustomCover(final String source
, final String type
) {
221 final Image
[] result
= new Image
[1];
224 new ConnectActionClientObject(host
, port
, key
) {
226 public void action(Version serverVersion
) throws Exception
{
227 Object rep
= send(new Object
[] { subkey
,
228 "GET_CUSTOM_COVER", type
, source
});
229 result
[0] = (Image
) rep
;
233 protected void onError(Exception e
) {
234 if (e
instanceof SSLException
) {
235 Instance
.getTraceHandler().error(
236 "Connection refused (bad key)");
238 Instance
.getTraceHandler().error(e
);
242 } catch (Exception e
) {
243 Instance
.getTraceHandler().error(e
);
250 public synchronized Story
getStory(final String luid
, Progress pg
) {
255 final Progress pgF
= pg
;
256 final Story
[] result
= new Story
[1];
259 new ConnectActionClientObject(host
, port
, key
) {
261 public void action(Version serverVersion
) throws Exception
{
267 Object rep
= send(new Object
[] { subkey
, "GET_STORY", luid
});
269 MetaData meta
= null;
270 if (rep
instanceof MetaData
) {
271 meta
= (MetaData
) rep
;
272 if (meta
.getWords() <= Integer
.MAX_VALUE
) {
273 pg
.setMinMax(0, (int) meta
.getWords());
277 List
<Object
> list
= new ArrayList
<Object
>();
278 for (Object obj
= send(null); obj
!= null; obj
= send(null)) {
283 result
[0] = RemoteLibraryServer
.rebuildStory(list
);
288 protected void onError(Exception e
) {
289 if (e
instanceof SSLException
) {
290 Instance
.getTraceHandler().error(
291 "Connection refused (bad key)");
293 Instance
.getTraceHandler().error(e
);
297 } catch (Exception e
) {
298 Instance
.getTraceHandler().error(e
);
305 public synchronized Story
save(final Story story
, final String luid
,
306 Progress pg
) throws IOException
{
311 final String
[] luidSaved
= new String
[1];
312 Progress pgSave
= new Progress();
313 Progress pgRefresh
= new Progress();
319 pg
.addProgress(pgSave
, 9);
320 pg
.addProgress(pgRefresh
, 1);
322 final Progress pgF
= pgSave
;
324 new ConnectActionClientObject(host
, port
, key
) {
326 public void action(Version serverVersion
) throws Exception
{
328 if (story
.getMeta().getWords() <= Integer
.MAX_VALUE
) {
329 pg
.setMinMax(0, (int) story
.getMeta().getWords());
332 send(new Object
[] { subkey
, "SAVE_STORY", luid
});
334 List
<Object
> list
= RemoteLibraryServer
.breakStory(story
);
335 for (Object obj
: list
) {
340 luidSaved
[0] = (String
) send(null);
346 protected void onError(Exception e
) {
347 if (e
instanceof SSLException
) {
348 Instance
.getTraceHandler().error(
349 "Connection refused (bad key)");
351 Instance
.getTraceHandler().error(e
);
356 // because the meta changed:
357 MetaData meta
= getInfo(luidSaved
[0]);
358 if (story
.getMeta().getClass() != null) {
359 // If already available locally:
360 meta
.setCover(story
.getMeta().getCover());
363 meta
.setCover(getCover(meta
.getLuid()));
373 public synchronized void delete(final String luid
) throws IOException
{
375 throw new IOException("Library not ready");
378 new ConnectActionClientObject(host
, port
, key
) {
380 public void action(Version serverVersion
) throws Exception
{
381 send(new Object
[] { subkey
, "DELETE_STORY", luid
});
385 protected void onError(Exception e
) {
386 if (e
instanceof SSLException
) {
387 Instance
.getTraceHandler().error(
388 "Connection refused (bad key)");
390 Instance
.getTraceHandler().error(e
);
397 public void setSourceCover(final String source
, final String luid
) {
402 setCover(source
, luid
, "SOURCE");
406 public void setAuthorCover(final String author
, final String luid
) {
411 setCover(author
, luid
, "AUTHOR");
414 // type = "SOURCE" | "AUTHOR"
415 private void setCover(final String value
, final String luid
,
422 new ConnectActionClientObject(host
, port
, key
) {
424 public void action(Version serverVersion
) throws Exception
{
425 send(new Object
[] { subkey
, "SET_COVER", type
, value
, luid
});
429 protected void onError(Exception e
) {
430 if (e
instanceof SSLException
) {
431 Instance
.getTraceHandler().error(
432 "Connection refused (bad key)");
434 Instance
.getTraceHandler().error(e
);
438 } catch (IOException e
) {
439 Instance
.getTraceHandler().error(e
);
444 // Could work (more slowly) without it
445 public Story
imprt(final URL url
, Progress pg
) throws IOException
{
450 // Import the file locally if it is actually a file
451 if (url
== null || url
.getProtocol().equalsIgnoreCase("file")) {
452 return super.imprt(url
, pg
);
455 // Import it remotely if it is an URL
462 Progress pgImprt
= new Progress();
463 Progress pgGet
= new Progress();
464 pg
.addProgress(pgImprt
, 1);
465 pg
.addProgress(pgGet
, 1);
467 final Progress pgF
= pgImprt
;
468 final String
[] luid
= new String
[1];
471 new ConnectActionClientObject(host
, port
, key
) {
473 public void action(Version serverVersion
) throws Exception
{
476 Object rep
= send(new Object
[] { subkey
, "IMPORT",
480 if (!RemoteLibraryServer
.updateProgress(pg
, rep
)) {
488 luid
[0] = (String
) rep
;
492 protected void onError(Exception e
) {
493 if (e
instanceof SSLException
) {
494 Instance
.getTraceHandler().error(
495 "Connection refused (bad key)");
497 Instance
.getTraceHandler().error(e
);
501 } catch (IOException e
) {
502 Instance
.getTraceHandler().error(e
);
505 if (luid
[0] == null) {
506 throw new IOException("Remote failure");
509 Story story
= getStory(luid
[0], pgGet
);
517 // Could work (more slowly) without it
518 protected synchronized void changeSTA(final String luid
,
519 final String newSource
, final String newTitle
,
520 final String newAuthor
, Progress pg
) throws IOException
{
525 final Progress pgF
= pg
== null ?
new Progress() : pg
;
528 new ConnectActionClientObject(host
, port
, key
) {
530 public void action(Version serverVersion
) throws Exception
{
533 Object rep
= send(new Object
[] { subkey
, "CHANGE_STA",
534 luid
, newSource
, newTitle
, newAuthor
});
536 if (!RemoteLibraryServer
.updateProgress(pg
, rep
)) {
545 protected void onError(Exception e
) {
546 if (e
instanceof SSLException
) {
547 Instance
.getTraceHandler().error(
548 "Connection refused (bad key)");
550 Instance
.getTraceHandler().error(e
);
554 } catch (IOException e
) {
555 Instance
.getTraceHandler().error(e
);
560 public synchronized File
getFile(final String luid
, Progress pg
) {
561 throw new java
.lang
.InternalError(
562 "Operation not supportorted on remote Libraries");
574 new ConnectActionClientObject(host
, port
, key
) {
576 public void action(Version serverVersion
) throws Exception
{
577 send(new Object
[] { subkey
, "EXIT" });
581 protected void onError(Exception e
) {
582 if (e
instanceof SSLException
) {
583 Instance
.getTraceHandler().error(
584 "Connection refused (bad key)");
586 Instance
.getTraceHandler().error(e
);
590 } catch (IOException e
) {
591 Instance
.getTraceHandler().error(e
);
596 public synchronized MetaData
getInfo(String luid
) {
601 List
<MetaData
> metas
= getMetasList(luid
, null);
602 if (!metas
.isEmpty()) {
610 protected List
<MetaData
> getMetas(Progress pg
) {
611 return getMetasList("*", pg
);
615 protected void updateInfo(MetaData meta
) {
616 // Will be taken care of directly server side
620 protected void invalidateInfo(String luid
) {
621 // Will be taken care of directly server side
624 // The following methods are only used by Save and Delete in BasicLibrary:
627 protected int getNextId() {
628 throw new java
.lang
.InternalError("Should not have been called");
632 protected void doDelete(String luid
) throws IOException
{
633 throw new java
.lang
.InternalError("Should not have been called");
637 protected Story
doSave(Story story
, Progress pg
) throws IOException
{
638 throw new java
.lang
.InternalError("Should not have been called");
644 * Return the meta of the given story or a list of all known metas if the
647 * Will not get the covers.
650 * the luid of the story or *
652 * the optional progress
657 private List
<MetaData
> getMetasList(final String luid
, Progress pg
) {
662 final Progress pgF
= pg
;
663 final List
<MetaData
> metas
= new ArrayList
<MetaData
>();
666 new ConnectActionClientObject(host
, port
, key
) {
668 public void action(Version serverVersion
) throws Exception
{
674 Object rep
= send(new Object
[] { subkey
, "GET_METADATA",
678 if (!RemoteLibraryServer
.updateProgress(pg
, rep
)) {
685 if (rep
instanceof MetaData
[]) {
686 for (MetaData meta
: (MetaData
[]) rep
) {
689 } else if (rep
!= null) {
690 metas
.add((MetaData
) rep
);
695 protected void onError(Exception e
) {
696 if (e
instanceof SSLException
) {
697 Instance
.getTraceHandler().error(
698 "Connection refused (bad key)");
700 Instance
.getTraceHandler().error(e
);
704 } catch (Exception e
) {
705 Instance
.getTraceHandler().error(e
);