1 package be
.nikiroo
.fanfix
.library
;
4 import java
.io
.IOException
;
6 import java
.net
.UnknownHostException
;
7 import java
.util
.ArrayList
;
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
;
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.
26 public class RemoteLibrary
extends BasicLibrary
{
29 private final String key
;
32 * Create a {@link RemoteLibrary} linked to the given server.
35 * the key that will allow us to exchange information with the
38 * the host to contact or NULL for localhost
40 * the port to contact it on
42 public RemoteLibrary(String key
, String host
, int port
) {
49 public String
getLibraryName() {
50 return host
+ ":" + port
;
54 public Status
getStatus() {
55 final Status
[] result
= new Status
[1];
57 result
[0] = Status
.INVALID
;
60 Instance
.getTraceHandler().trace("Getting remote lib status...");
61 new ConnectActionClientObject(host
, port
, false) {
63 public void action(Version serverVersion
) throws Exception
{
65 Object rep
= sendCmd(this, new Object
[] { "PING" });
67 if ("PONG".equals(rep
)) {
68 result
[0] = Status
.READY
;
70 result
[0] = Status
.UNAUTORIZED
;
72 } catch (IllegalArgumentException e
) {
73 result
[0] = Status
.UNAUTORIZED
;
78 protected void onError(Exception e
) {
79 result
[0] = Status
.UNAVAILABLE
;
82 } catch (UnknownHostException e
) {
83 result
[0] = Status
.INVALID
;
84 } catch (IllegalArgumentException e
) {
85 result
[0] = Status
.INVALID
;
86 } catch (Exception e
) {
87 result
[0] = Status
.UNAVAILABLE
;
90 Instance
.getTraceHandler().trace("Remote lib status: " + result
[0]);
95 public Image
getCover(final String luid
) {
96 final Image
[] result
= new Image
[1];
99 new ConnectActionClientObject(host
, port
, false) {
101 public void action(Version serverVersion
) throws Exception
{
102 Object rep
= sendCmd(this,
103 new Object
[] { "GET_COVER", luid
});
104 result
[0] = (Image
) rep
;
108 protected void onError(Exception e
) {
109 Instance
.getTraceHandler().error(e
);
112 } catch (Exception e
) {
113 Instance
.getTraceHandler().error(e
);
120 public Image
getCustomSourceCover(final String source
) {
121 return getCustomCover(source
, "SOURCE");
125 public Image
getCustomAuthorCover(final String author
) {
126 return getCustomCover(author
, "AUTHOR");
129 // type: "SOURCE" or "AUTHOR"
130 private Image
getCustomCover(final String source
, final String type
) {
131 final Image
[] result
= new Image
[1];
134 new ConnectActionClientObject(host
, port
, false) {
136 public void action(Version serverVersion
) throws Exception
{
137 Object rep
= sendCmd(this,
138 new Object
[] { "GET_CUSTOM_COVER", type
, source
});
139 result
[0] = (Image
) rep
;
143 protected void onError(Exception e
) {
144 Instance
.getTraceHandler().error(e
);
147 } catch (Exception e
) {
148 Instance
.getTraceHandler().error(e
);
155 public synchronized Story
getStory(final String luid
, Progress pg
) {
156 final Progress pgF
= pg
;
157 final Story
[] result
= new Story
[1];
160 new ConnectActionClientObject(host
, port
, false) {
162 public void action(Version serverVersion
) throws Exception
{
168 Object rep
= sendCmd(this,
169 new Object
[] { "GET_STORY", luid
});
171 MetaData meta
= null;
172 if (rep
instanceof MetaData
) {
173 meta
= (MetaData
) rep
;
174 if (meta
.getWords() <= Integer
.MAX_VALUE
) {
175 pg
.setMinMax(0, (int) meta
.getWords());
179 List
<Object
> list
= new ArrayList
<Object
>();
180 for (Object obj
= send(null); obj
!= null; obj
= send(
186 result
[0] = RemoteLibraryServer
.rebuildStory(list
);
191 protected void onError(Exception e
) {
192 Instance
.getTraceHandler().error(e
);
195 } catch (Exception e
) {
196 Instance
.getTraceHandler().error(e
);
203 public synchronized Story
save(final Story story
, final String luid
,
204 Progress pg
) throws IOException
{
205 final String
[] luidSaved
= new String
[1];
206 Progress pgSave
= new Progress();
207 Progress pgRefresh
= new Progress();
213 pg
.addProgress(pgSave
, 9);
214 pg
.addProgress(pgRefresh
, 1);
216 final Progress pgF
= pgSave
;
218 new ConnectActionClientObject(host
, port
, false) {
220 public void action(Version serverVersion
) throws Exception
{
222 if (story
.getMeta().getWords() <= Integer
.MAX_VALUE
) {
223 pg
.setMinMax(0, (int) story
.getMeta().getWords());
226 sendCmd(this, new Object
[] { "SAVE_STORY", luid
});
228 List
<Object
> list
= RemoteLibraryServer
.breakStory(story
);
229 for (Object obj
: list
) {
234 luidSaved
[0] = (String
) send(null);
240 protected void onError(Exception e
) {
241 Instance
.getTraceHandler().error(e
);
245 // because the meta changed:
246 MetaData meta
= getInfo(luidSaved
[0]);
247 if (story
.getMeta().getClass() != null) {
248 // If already available locally:
249 meta
.setCover(story
.getMeta().getCover());
252 meta
.setCover(getCover(meta
.getLuid()));
262 public synchronized void delete(final String luid
) throws IOException
{
263 new ConnectActionClientObject(host
, port
, false) {
265 public void action(Version serverVersion
) throws Exception
{
266 sendCmd(this, new Object
[] { "DELETE_STORY", luid
});
270 protected void onError(Exception e
) {
271 Instance
.getTraceHandler().error(e
);
277 public void setSourceCover(final String source
, final String luid
) {
278 setCover(source
, luid
, "SOURCE");
282 public void setAuthorCover(final String author
, final String luid
) {
283 setCover(author
, luid
, "AUTHOR");
286 // type = "SOURCE" | "AUTHOR"
287 private void setCover(final String value
, final String luid
,
290 new ConnectActionClientObject(host
, port
, false) {
292 public void action(Version serverVersion
) throws Exception
{
294 new Object
[] { "SET_COVER", type
, value
, luid
});
298 protected void onError(Exception e
) {
299 Instance
.getTraceHandler().error(e
);
302 } catch (IOException e
) {
303 Instance
.getTraceHandler().error(e
);
308 // Could work (more slowly) without it
309 public Story
imprt(final URL url
, Progress pg
) throws IOException
{
310 // Import the file locally if it is actually a file
311 if (url
== null || url
.getProtocol().equalsIgnoreCase("file")) {
312 return super.imprt(url
, pg
);
315 // Import it remotely if it is an URL
322 Progress pgImprt
= new Progress();
323 Progress pgGet
= new Progress();
324 pg
.addProgress(pgImprt
, 1);
325 pg
.addProgress(pgGet
, 1);
327 final Progress pgF
= pgImprt
;
328 final String
[] luid
= new String
[1];
331 new ConnectActionClientObject(host
, port
, false) {
333 public void action(Version serverVersion
) throws Exception
{
336 Object rep
= sendCmd(this,
337 new Object
[] { "IMPORT", url
.toString() });
340 if (!RemoteLibraryServer
.updateProgress(pg
, rep
)) {
348 luid
[0] = (String
) rep
;
352 protected void onError(Exception e
) {
353 Instance
.getTraceHandler().error(e
);
356 } catch (IOException e
) {
357 Instance
.getTraceHandler().error(e
);
360 if (luid
[0] == null) {
361 throw new IOException("Remote failure");
364 Story story
= getStory(luid
[0], pgGet
);
372 // Could work (more slowly) without it
373 protected synchronized void changeSTA(final String luid
,
374 final String newSource
, final String newTitle
,
375 final String newAuthor
, Progress pg
) throws IOException
{
376 final Progress pgF
= pg
== null ?
new Progress() : pg
;
379 new ConnectActionClientObject(host
, port
, false) {
381 public void action(Version serverVersion
) throws Exception
{
384 Object rep
= sendCmd(this, new Object
[] { "CHANGE_STA",
385 luid
, newSource
, newTitle
, newAuthor
});
387 if (!RemoteLibraryServer
.updateProgress(pg
, rep
)) {
396 protected void onError(Exception e
) {
397 Instance
.getTraceHandler().error(e
);
400 } catch (IOException e
) {
401 Instance
.getTraceHandler().error(e
);
406 public synchronized File
getFile(final String luid
, Progress pg
) {
407 throw new java
.lang
.InternalError(
408 "Operation not supportorted on remote Libraries");
416 new ConnectActionClientObject(host
, port
, false) {
418 public void action(Version serverVersion
) throws Exception
{
419 sendCmd(this, new Object
[] { "EXIT" });
423 protected void onError(Exception e
) {
424 Instance
.getTraceHandler().error(e
);
427 } catch (IOException e
) {
428 Instance
.getTraceHandler().error(e
);
433 public synchronized MetaData
getInfo(String luid
) {
434 List
<MetaData
> metas
= getMetasList(luid
, null);
435 if (!metas
.isEmpty()) {
443 protected List
<MetaData
> getMetas(Progress pg
) {
444 return getMetasList("*", pg
);
448 protected void updateInfo(MetaData meta
) {
449 // Will be taken care of directly server side
453 protected void invalidateInfo(String luid
) {
454 // Will be taken care of directly server side
457 // The following methods are only used by Save and Delete in BasicLibrary:
460 protected int getNextId() {
461 throw new java
.lang
.InternalError("Should not have been called");
465 protected void doDelete(String luid
) throws IOException
{
466 throw new java
.lang
.InternalError("Should not have been called");
470 protected Story
doSave(Story story
, Progress pg
) throws IOException
{
471 throw new java
.lang
.InternalError("Should not have been called");
477 * Return the meta of the given story or a list of all known metas if the
480 * Will not get the covers.
483 * the luid of the story or *
485 * the optional progress
490 private List
<MetaData
> getMetasList(final String luid
, Progress pg
) {
491 final Progress pgF
= pg
;
492 final List
<MetaData
> metas
= new ArrayList
<MetaData
>();
495 new ConnectActionClientObject(host
, port
, false) {
497 public void action(Version serverVersion
) throws Exception
{
504 Object rep
= sendCmd(this,
505 new Object
[] { "GET_METADATA", luid
});
508 if (!RemoteLibraryServer
.updateProgress(pg
, rep
)) {
515 if (rep
instanceof MetaData
[]) {
516 for (MetaData meta
: (MetaData
[]) rep
) {
519 } else if (rep
!= null) {
520 metas
.add((MetaData
) rep
);
525 protected void onError(Exception e
) {
526 Instance
.getTraceHandler().error(e
);
529 } catch (Exception e
) {
530 Instance
.getTraceHandler().error(e
);
536 // IllegalArgumentException if key is bad
537 private Object
sendCmd(ConnectActionClientObject action
, Object
[] params
)
538 throws IOException
, NoSuchFieldException
, NoSuchMethodException
,
539 ClassNotFoundException
{
540 Object rep
= action
.send(params
);
542 String hash
= hashKey(key
, "" + rep
);
543 rep
= action
.send(hash
);
545 throw new java
.lang
.IllegalArgumentException();
548 return action
.send(hash
);
552 * Return a hash that corresponds to the given key and the given random
556 * the key (the secret)
561 * @return a hash that was computed using both
563 static String
hashKey(String key
, String random
) {
564 return StringUtils
.getMd5Hash(key
+ " <==> " + random
);