View Javadoc
1   package com.github.celldynamics.quimp.omero;
2   
3   import java.io.IOException;
4   import java.net.URISyntaxException;
5   import java.nio.file.Path;
6   import java.nio.file.Paths;
7   import java.util.Base64.Decoder;
8   import java.util.Base64.Encoder;
9   import java.util.List;
10  import java.util.concurrent.ExecutionException;
11  
12  import javax.swing.JDialog;
13  
14  import org.slf4j.Logger;
15  import org.slf4j.LoggerFactory;
16  
17  import com.github.celldynamics.quimp.QuimP;
18  import com.github.celldynamics.quimp.QuimpException;
19  import com.github.celldynamics.quimp.QuimpException.MessageSinkTypes;
20  import com.github.celldynamics.quimp.filesystem.FileExtensions;
21  import com.github.celldynamics.quimp.filesystem.QconfLoader;
22  
23  import Glacier2.CannotCreateSessionException;
24  import Glacier2.PermissionDeniedException;
25  import ij.IJ;
26  import ij.Prefs;
27  import omero.ServerError;
28  import omero.gateway.exception.DSAccessException;
29  import omero.gateway.exception.DSOutOfServiceException;
30  import omero.gateway.model.DatasetData;
31  import omero.gateway.model.ImageData;
32  
33  /**
34   * QuimP module for Omero integration.
35   * 
36   * @author p.baniukiewicz
37   *
38   */
39  public class OmeroClient_ {
40    static final Logger LOGGER = LoggerFactory.getLogger(OmeroClient_.class.getName());
41    static final MessageSinkTypes SOURCE = QuimpException.MessageSinkTypes.GUI;
42    private static final String KEY = "sEcReT";
43    private static final String PREFS_PREFIX = "omero";
44    private static final String PREFS_USER_SUFFIX = ".user";
45    private static final String PREFS_HOST_SUFFIX = ".host";
46    private static final String PREFS_PASS_SUFFIX = ".pass";
47    private static final String PREFS_PORT_SUFFIX = ".port";
48    private String user;
49    private String host;
50    private String pass;
51    private int port;
52    private OmeroLoginDialog dialog;
53    private AbstractDataSet<DatasetData> currentDatasets;
54    private AbstractDataSet<ImageData> currentImages;
55    OmeroBrowser omero;
56  
57    /**
58     * Create all instances of UI and API.
59     */
60    public OmeroClient_() {
61      downloadPrefs();
62      dialog = new OmeroLoginDialog(this);
63      dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
64      dialog.setVisible(true);
65      currentDatasets = new AbstractDataSet<>();
66      currentImages = new AbstractDataSet<>();
67    }
68  
69    boolean connect() {
70      LOGGER.trace("Connecting..");
71      currentDatasets.clear();
72      currentImages.clear();
73      try {
74        disconnect(); // close previous
75        omero = new OmeroBrowser(user, pass, host, port);
76        omero.connect();
77        LOGGER.info("Connected!");
78        return true;
79      } catch (Exception e) {
80        omero.silentClose();
81        if (!(e instanceof QuimpException)) {
82          LOGGER.debug(e.getMessage(), e);
83          QuimpException.showGuiWithMessage(null, QuimpException.prepareMessage(e, "OmeroClient"));
84        } else {
85          QuimpException./../../../com/github/celldynamics/quimp/QuimpException.html#QuimpException">QuimpException ex = (QuimpException) e;
86          ex.setMessageSinkType(SOURCE);
87          ex.handleException(IJ.getInstance(), "OmeroClient");
88        }
89      }
90      return false;
91    }
92  
93    void disconnect() {
94      LOGGER.trace("Closing..");
95      if (omero != null) {
96        omero.silentClose();
97      }
98    }
99  
100   /**
101    * Get datasets from Omero and prepare data to show in UI.
102    * 
103    * <p>Updates also internal list of current datasets.
104    * 
105    * @return List of datasets.
106    */
107   List<DatasetData> getDatasets() {
108     if (omero != null) {
109       try {
110         currentDatasets.clear();
111         currentDatasets.ds.addAll(omero.listDatasets());
112       } catch (DSOutOfServiceException | DSAccessException | ExecutionException e) {
113         LOGGER.debug(e.getMessage(), e);
114         QuimpException.showGuiWithMessage(IJ.getInstance(),
115                 QuimpException.prepareMessage(e, "OmeroClient"));
116       }
117     }
118     return currentDatasets.ds;
119   }
120 
121   /**
122    * Return images from datasetName in format [name,date].
123    * 
124    * <p>Output is used to fill {@link OmeroLoginDialog#getTableImagesDownload()}. Updates also
125    * internal list
126    * of images for current dataset.
127    * 
128    * @param datasetIndex index of dataset returned by {@link #getDatasets()}.
129    * @return images from dataset as list
130    * @see #getDatasets()
131    */
132   List<ImageData> getImages(int datasetIndex) {
133     DatasetData dstmp = currentDatasets.ds.get(datasetIndex);
134     try {
135       currentImages.clear();
136       currentImages.ds.addAll(omero.openDataset(dstmp));
137     } catch (DSOutOfServiceException | DSAccessException | ExecutionException e) {
138       LOGGER.debug(e.getMessage(), e);
139       QuimpException.showGuiWithMessage(IJ.getInstance(),
140               QuimpException.prepareMessage(e, "OmeroClient"));
141     }
142     return currentImages.ds;
143   }
144 
145   /**
146    * Upload current preferences to IJ_prefs or clear uploaded keys.
147    * 
148    * @param clear clear if true, upload otherwise
149    */
150   void uploadPrefs(boolean clear) {
151     if (clear) {
152       Prefs.set(PREFS_PREFIX + QuimP.QUIMP_PREFS_SUFFIX + PREFS_USER_SUFFIX, "");
153       Prefs.set(PREFS_PREFIX + QuimP.QUIMP_PREFS_SUFFIX + PREFS_HOST_SUFFIX, "");
154       Prefs.set(PREFS_PREFIX + QuimP.QUIMP_PREFS_SUFFIX + PREFS_PASS_SUFFIX, "");
155       Prefs.set(PREFS_PREFIX + QuimP.QUIMP_PREFS_SUFFIX + PREFS_PORT_SUFFIX, OmeroBrowser.DEF_PORT);
156       LOGGER.info("Prefs cleared");
157     } else {
158       Prefs.set(PREFS_PREFIX + QuimP.QUIMP_PREFS_SUFFIX + PREFS_USER_SUFFIX, user);
159       Prefs.set(PREFS_PREFIX + QuimP.QUIMP_PREFS_SUFFIX + PREFS_HOST_SUFFIX, host);
160       Prefs.set(PREFS_PREFIX + QuimP.QUIMP_PREFS_SUFFIX + PREFS_PASS_SUFFIX,
161               Xor.encrypt(pass, KEY));
162       Prefs.set(PREFS_PREFIX + QuimP.QUIMP_PREFS_SUFFIX + PREFS_PORT_SUFFIX, port);
163       LOGGER.debug("Prefs uploaded");
164     }
165 
166   }
167 
168   void downloadPrefs() {
169     user = Prefs.get(PREFS_PREFIX + QuimP.QUIMP_PREFS_SUFFIX + PREFS_USER_SUFFIX, "");
170     host = Prefs.get(PREFS_PREFIX + QuimP.QUIMP_PREFS_SUFFIX + PREFS_HOST_SUFFIX, "");
171     pass = Xor.decrypt(Prefs.get(PREFS_PREFIX + QuimP.QUIMP_PREFS_SUFFIX + PREFS_PASS_SUFFIX, ""),
172             KEY);
173     port = (int) Prefs.get(PREFS_PREFIX + QuimP.QUIMP_PREFS_SUFFIX + PREFS_PORT_SUFFIX,
174             OmeroBrowser.DEF_PORT);
175   }
176 
177   /**
178    * Set user.
179    * 
180    * @param user the user to set
181    */
182   void setUser(String user) {
183     LOGGER.debug("Set user: " + user);
184     this.user = user;
185     if (dialog.getChckbxStoreCred().isSelected()) {
186       uploadPrefs(false);
187     }
188   }
189 
190   /**
191    * Set host.
192    * 
193    * @param host the host to set
194    */
195   void setHost(String host) {
196     LOGGER.debug("Set host: " + host);
197     this.host = host;
198     if (dialog.getChckbxStoreCred().isSelected()) {
199       uploadPrefs(false);
200     }
201 
202   }
203 
204   /**
205    * Set pass.
206    * 
207    * @param pass the pass to set
208    */
209   void setPass(String pass) {
210     LOGGER.debug("Set pass: " + pass);
211     this.pass = pass;
212     if (dialog.getChckbxStoreCred().isSelected()) {
213       uploadPrefs(false);
214     }
215   }
216 
217   /**
218    * Set port.
219    * 
220    * @param port the port to set
221    */
222   void setPort(String port) {
223     LOGGER.debug("Set port: " + port);
224     if (!port.isEmpty()) {
225       this.port = Integer.parseInt(port);
226     }
227     if (dialog.getChckbxStoreCred().isSelected()) {
228       uploadPrefs(false);
229     }
230   }
231 
232   /**
233    * Get user field.
234    * 
235    * @return the user
236    */
237   String getUser() {
238     return user;
239   }
240 
241   /**
242    * Get host field.
243    * 
244    * @return the host
245    */
246   String getHost() {
247     return host;
248   }
249 
250   /**
251    * Get pass field.
252    * 
253    * @return the pass
254    */
255   String getPass() {
256     return pass;
257   }
258 
259   /**
260    * Get port field.
261    * 
262    * @return the port
263    */
264   String getPort() {
265     return String.valueOf(port);
266   }
267 
268   /**
269    * Get id of selected dataset.
270    * 
271    * @return the currentDs
272    */
273   AbstractDataSet<DatasetData> getCurrentDatasets() {
274     return currentDatasets;
275   }
276 
277   /**
278    * Set id of selected dataset.
279    * 
280    * <p>This is index of dataset returned by {@link #getDatasets()}.
281    * 
282    * @param currentDs the currentDs to set
283    */
284   void setCurrentDs(int currentDs) {
285     this.currentDatasets.currentEl = currentDs;
286   }
287 
288   /**
289    * Get d of selected image.
290    * 
291    * @return the currentIm
292    */
293   AbstractDataSet<ImageData> getCurrentImages() {
294     return currentImages;
295   }
296 
297   /**
298    * Set id of selected image.
299    * 
300    * <p>This is index of image returned by {@link #getCurrentImages()}.
301    * 
302    * @param currentIm the currentIm to set
303    */
304   void setCurrentIm(int currentIm) {
305     this.currentImages.currentEl = currentIm;
306   }
307 
308   /**
309    * Simple XOR implementation to obfuscate password in IJ_Prefs.
310    * 
311    * @author p.baniukiewicz
312    *
313    */
314   static class Xor {
315 
316     /**
317      * Encrypt string.
318      * 
319      * @param text string to encrypt
320      * @param key password
321      * @return base64 encrypted string
322      */
323     public static String encrypt(String text, String key) {
324       Encoder enc = java.util.Base64.getEncoder();
325       return enc.encodeToString(xor(text.getBytes(), key.getBytes()));
326     }
327 
328     /**
329      * Decrypt string.
330      * 
331      * @param text string base64 to decrypt
332      * @param key password
333      * @return decrypted string
334      */
335     public static String decrypt(String text, String key) {
336       Decoder d = java.util.Base64.getDecoder();
337       return new String(xor(d.decode(text), key.getBytes()));
338     }
339 
340     private static byte[] xor(byte[] text, byte[] key) {
341       byte[] ret = new byte[text.length];
342       for (int i = 0; i < text.length; i++) {
343         ret[i] = (byte) (text[i] ^ key[i % key.length]);
344       }
345       return ret;
346     }
347 
348   }
349 
350   /**
351    * Downloads image.
352    * 
353    * <p>Image selected by {@link OmeroClient_#setCurrentDs(int)} and then
354    * {@link OmeroClient_#setCurrentIm(int)}
355    * 
356    * @param destFolder destination folder
357    */
358   public void download(String destFolder) {
359     if (currentDatasets.validate() && currentImages.validate()) {
360       LOGGER.debug("Download: " + currentDatasets.toString() + ", " + currentImages.toString());
361       try {
362         omero.download(currentImages.getCurrent(), Paths.get(destFolder));
363       } catch (ServerError | PermissionDeniedException | CannotCreateSessionException
364               | DSOutOfServiceException | DSAccessException | ExecutionException | IOException
365               | URISyntaxException e) {
366         LOGGER.debug(e.getMessage(), e);
367         QuimpException.showGuiWithMessage(null, QuimpException.prepareMessage(e, "OmeroClient"));
368       }
369     } else {
370 
371       QuimpException.showGuiWithMessage(null,
372               "Connect to database first and then select dataset on left and image "
373                       + "with attached QCONF on right panel.");
374     }
375 
376   }
377 
378   /**
379    * Upload qconf and related image to currently selected dataset.
380    * 
381    * @see #setCurrentDs(int)
382    */
383   public void upload() {
384     if (currentDatasets.validate()) {
385       try {
386         QconfLoaderystem/QconfLoader.html#QconfLoader">QconfLoader qconfLoader = new QconfLoader(null, FileExtensions.newConfigFileExt);
387         if (qconfLoader.isFileLoaded() == QconfLoader.QCONF_INVALID) {
388           return;
389         }
390         Path qconfPath = qconfLoader.getQconfFile();
391         qconfLoader.getImage(); // try to read image from qconf and ask to point if abs path wrong
392         Path imagePath = qconfLoader.getBOA().boap.getOrgFile().toPath();
393         LOGGER.debug("Upload " + qconfPath.toString() + ", " + imagePath.toString());
394         if (omero != null && currentDatasets.validate() && imagePath.getFileName() != null) {
395           omero.upload(new String[] { imagePath.toString() }, currentDatasets.getCurrent());
396           omero.upload(imagePath.getFileName().toString(), qconfPath.toString(),
397                   currentDatasets.getCurrent());
398         }
399       } catch (QuimpException e) {
400         e.setMessageSinkType(SOURCE);
401         e.handleException(IJ.getInstance(), "OmeroClient");
402       } catch (Exception e) {
403         LOGGER.debug(e.getMessage(), e);
404         QuimpException.showGuiWithMessage(null, QuimpException.prepareMessage(e, "OmeroClient"));
405       }
406     } else {
407       QuimpException.showGuiWithMessage(null,
408               "Connect to database first and then select dataset on left panel.");
409     }
410   }
411 }