Improve CacheLibrary Save()
[nikiroo-utils.git] / src / be / nikiroo / fanfix / library / RemoteLibraryServer.java
CommitLineData
e42573a0 1package be.nikiroo.fanfix.library;
b0e88ebd 2
b0e88ebd 3import java.io.IOException;
085a2f9a 4import java.security.InvalidParameterException;
74a40dfb 5import java.util.ArrayList;
68e2c6d2 6import java.util.List;
b0e88ebd 7
e42573a0 8import be.nikiroo.fanfix.Instance;
74a40dfb 9import be.nikiroo.fanfix.data.Chapter;
b0e88ebd 10import be.nikiroo.fanfix.data.MetaData;
74a40dfb 11import be.nikiroo.fanfix.data.Paragraph;
085a2f9a 12import be.nikiroo.fanfix.data.Story;
b9ce9cad
NR
13import be.nikiroo.utils.Progress;
14import be.nikiroo.utils.Progress.ProgressListener;
b0e88ebd 15import be.nikiroo.utils.Version;
62c63b07
NR
16import be.nikiroo.utils.serial.server.ConnectActionServerObject;
17import be.nikiroo.utils.serial.server.ServerObject;
b0e88ebd 18
a85e8077
NR
19/**
20 * Create a new remote server that will listen for order on the given port.
21 * <p>
2a25f781
NR
22 * The available commands are given as arrays of objects (first item is the key,
23 * second is the command, the rest are the arguments).
24 * <p>
25 * The key is always a String, the commands are also Strings; the parameters
26 * vary depending upon the command.
a85e8077 27 * <ul>
2a25f781 28 * <li>[key] GET_METADATA *: will return the metadata of all the stories in the
085a2f9a 29 * library</li>
2a25f781 30 * <li>[key] GET_STORY [luid]: will return the given story if it exists (or NULL
2070ced5 31 * if not)</li>
74a40dfb
NR
32 * <li>[key] SAVE_STORY [luid]: save the story (that must be sent just after the
33 * command) with the given LUID</li>
2a25f781
NR
34 * <li>[key] DELETE_STORY [luid]: delete the story of LUID luid</li>
35 * <li>[key] GET_COVER [luid]: return the cover of the story</li>
36 * <li>[key] GET_SOURCE_COVER [source]: return the cover for this source</li>
37 * <li>[key] SET_SOURCE_COVER [source], [luid]: set the default cover for the
2070ced5 38 * given source to the cover of the story denoted by luid</li>
2a25f781 39 * <li>[key] EXIT: stop the server</li>
a85e8077
NR
40 * </ul>
41 *
42 * @author niki
43 */
62c63b07 44public class RemoteLibraryServer extends ServerObject {
2070ced5 45 private final String key;
b0e88ebd 46
a85e8077
NR
47 /**
48 * Create a new remote server (will not be active until
49 * {@link RemoteLibraryServer#start()} is called).
50 *
2070ced5
NR
51 * @param key
52 * the key that will restrict access to this server
a85e8077
NR
53 * @param port
54 * the port to listen on
55 *
56 * @throws IOException
57 * in case of I/O error
58 */
2070ced5 59 public RemoteLibraryServer(String key, int port) throws IOException {
22b2b942 60 super("Fanfix remote library", port, true);
2070ced5 61 this.key = key;
b9ce9cad
NR
62
63 setTraceHandler(Instance.getTraceHandler());
b0e88ebd
NR
64 }
65
66 @Override
62c63b07 67 protected Object onRequest(ConnectActionServerObject action,
b0e88ebd 68 Version clientVersion, Object data) throws Exception {
2070ced5 69 String key = "";
085a2f9a
NR
70 String command = "";
71 Object[] args = new Object[0];
72 if (data instanceof Object[]) {
2070ced5
NR
73 Object[] dataArray = (Object[]) data;
74 if (dataArray.length >= 2) {
75 args = new Object[dataArray.length - 2];
76 for (int i = 2; i < dataArray.length; i++) {
f569d249 77 args[i - 2] = dataArray[i];
2070ced5
NR
78 }
79
80 key = "" + dataArray[0];
81 command = "" + dataArray[1];
b0e88ebd
NR
82 }
83 }
84
b9ce9cad 85 String trace = "[" + command + "] ";
085a2f9a 86 for (Object arg : args) {
b9ce9cad 87 trace += arg + " ";
085a2f9a 88 }
b9ce9cad 89 getTraceHandler().trace(trace);
b0e88ebd 90
2070ced5 91 if (!key.equals(this.key)) {
b9ce9cad
NR
92 getTraceHandler().trace("Key rejected.");
93 return null;
2070ced5
NR
94 }
95
a85e8077 96 if ("GET_METADATA".equals(command)) {
2070ced5 97 if (args[0].equals("*")) {
b9ce9cad
NR
98 List<MetaData> metas = Instance.getLibrary().getMetas(
99 createPgForwarder(action));
a85e8077
NR
100 return metas.toArray(new MetaData[] {});
101 }
085a2f9a 102 throw new InvalidParameterException(
2070ced5 103 "only * is valid here, but you passed: " + args[0]);
a85e8077 104 } else if ("GET_STORY".equals(command)) {
b9ce9cad
NR
105 MetaData meta = Instance.getLibrary().getInfo("" + args[0]);
106 meta = meta.clone();
107 meta.setCover(null);
108
109 action.send(meta);
110 action.rec();
111
74a40dfb 112 Story story = Instance.getLibrary().getStory("" + args[0], null);
b9ce9cad
NR
113 for (Object obj : breakStory(story)) {
114 action.send(obj);
115 action.rec();
116 }
085a2f9a 117 } else if ("SAVE_STORY".equals(command)) {
b9ce9cad
NR
118 List<Object> list = new ArrayList<Object>();
119
120 action.send(null);
121 Object obj = action.rec();
122 while (obj != null) {
123 list.add(obj);
124 action.send(null);
125 obj = action.rec();
126 }
127
128 Story story = rebuildStory(list);
74a40dfb 129 Instance.getLibrary().save(story, "" + args[1], null);
085a2f9a 130 } else if ("DELETE_STORY".equals(command)) {
2070ced5 131 Instance.getLibrary().delete("" + args[0]);
e604986c 132 } else if ("GET_COVER".equals(command)) {
2070ced5 133 return Instance.getLibrary().getCover("" + args[0]);
085a2f9a 134 } else if ("GET_SOURCE_COVER".equals(command)) {
2070ced5 135 return Instance.getLibrary().getSourceCover("" + args[0]);
085a2f9a 136 } else if ("SET_SOURCE_COVER".equals(command)) {
2070ced5 137 Instance.getLibrary().setSourceCover("" + args[0], "" + args[1]);
5e848e6a
NR
138 } else if ("EXIT".equals(command)) {
139 stop(0, false);
b0e88ebd
NR
140 }
141
142 return null;
143 }
74a40dfb 144
b9ce9cad
NR
145 @Override
146 protected void onError(Exception e) {
147 getTraceHandler().error(e);
148 }
74a40dfb 149
b9ce9cad
NR
150 /**
151 * Break a story in multiple {@link Object}s for easier serialisation.
152 *
153 * @param story
154 * the {@link Story} to break
155 *
156 * @return the list of {@link Object}s
157 */
158 static List<Object> breakStory(Story story) {
159 List<Object> list = new ArrayList<Object>();
74a40dfb
NR
160
161 story = story.clone();
b9ce9cad 162 list.add(story);
74a40dfb 163
b9ce9cad
NR
164 if (story.getMeta().isImageDocument()) {
165 for (Chapter chap : story) {
166 list.add(chap);
167 list.addAll(chap.getParagraphs());
168 chap.setParagraphs(new ArrayList<Paragraph>());
74a40dfb 169 }
b9ce9cad 170 story.setChapters(new ArrayList<Chapter>());
74a40dfb 171 }
74a40dfb 172
b9ce9cad
NR
173 return list;
174 }
74a40dfb 175
b9ce9cad
NR
176 /**
177 * Rebuild a story from a list of broke up {@link Story} parts.
178 *
179 * @param list
180 * the list of {@link Story} parts
181 *
182 * @return the reconstructed {@link Story}
183 */
184 static Story rebuildStory(List<Object> list) {
74a40dfb 185 Story story = null;
b9ce9cad 186 Chapter chap = null;
74a40dfb 187
b9ce9cad
NR
188 for (Object obj : list) {
189 if (obj instanceof Story) {
190 story = (Story) obj;
191 } else if (obj instanceof Chapter) {
192 chap = (Chapter) obj;
193 story.getChapters().add(chap);
194 } else if (obj instanceof Paragraph) {
195 chap.getParagraphs().add((Paragraph) obj);
74a40dfb
NR
196 }
197 }
198
199 return story;
200 }
201
b9ce9cad
NR
202 /**
203 * Update the {@link Progress} with the adequate {@link Object} received
204 * from the network via {@link RemoteLibraryServer}.
205 *
206 * @param pg
207 * the {@link Progress} to update
208 * @param rep
209 * the object received from the network
210 *
211 * @return TRUE if it was a progress event, FALSE if not
212 */
213 static boolean updateProgress(Progress pg, Object rep) {
214 if (rep instanceof Integer[]) {
215 Integer[] a = (Integer[]) rep;
216 if (a.length == 3) {
217 int min = a[0];
218 int max = a[1];
219 int progress = a[2];
220
221 if (min >= 0 && min <= max) {
222 pg.setMinMax(min, max);
223 pg.setProgress(progress);
224
225 return true;
226 }
227 }
74a40dfb 228 }
b9ce9cad
NR
229
230 return false;
74a40dfb
NR
231 }
232
b9ce9cad
NR
233 /**
234 * Create a {@link Progress} that will forward its progress over the
235 * network.
236 *
237 * @param action
238 * the {@link ConnectActionServerObject} to use to forward it
239 *
240 * @return the {@link Progress}
241 */
242 private static Progress createPgForwarder(
243 final ConnectActionServerObject action) {
244 final Progress pg = new Progress();
245 final Integer[] p = new Integer[] { -1, -1, -1 };
246 pg.addProgressListener(new ProgressListener() {
247 @Override
248 public void progress(Progress progress, String name) {
249 int min = pg.getMin();
250 int max = pg.getMax();
251 int relativeProgress = min
252 + (int) Math.round(pg.getRelativeProgress()
253 * (max - min));
254
255 // Do not re-send the same value twice over the wire
256 if (p[0] != min || p[1] != max || p[2] != relativeProgress) {
257 p[0] = min;
258 p[1] = max;
259 p[2] = relativeProgress;
260
261 try {
262 action.send(new Integer[] { min, max, relativeProgress });
263 action.rec();
264 } catch (Exception e) {
265 Instance.getTraceHandler().error(e);
266 }
267 }
268 }
269 });
270
271 return pg;
74a40dfb 272 }
b0e88ebd 273}