OmeroClient_.java
package com.github.celldynamics.quimp.omero;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Base64.Decoder;
import java.util.Base64.Encoder;
import java.util.List;
import java.util.concurrent.ExecutionException;
import javax.swing.JDialog;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.github.celldynamics.quimp.QuimP;
import com.github.celldynamics.quimp.QuimpException;
import com.github.celldynamics.quimp.QuimpException.MessageSinkTypes;
import com.github.celldynamics.quimp.filesystem.FileExtensions;
import com.github.celldynamics.quimp.filesystem.QconfLoader;
import Glacier2.CannotCreateSessionException;
import Glacier2.PermissionDeniedException;
import ij.IJ;
import ij.Prefs;
import omero.ServerError;
import omero.gateway.exception.DSAccessException;
import omero.gateway.exception.DSOutOfServiceException;
import omero.gateway.model.DatasetData;
import omero.gateway.model.ImageData;
/**
* QuimP module for Omero integration.
*
* @author p.baniukiewicz
*
*/
public class OmeroClient_ {
static final Logger LOGGER = LoggerFactory.getLogger(OmeroClient_.class.getName());
static final MessageSinkTypes SOURCE = QuimpException.MessageSinkTypes.GUI;
private static final String KEY = "sEcReT";
private static final String PREFS_PREFIX = "omero";
private static final String PREFS_USER_SUFFIX = ".user";
private static final String PREFS_HOST_SUFFIX = ".host";
private static final String PREFS_PASS_SUFFIX = ".pass";
private static final String PREFS_PORT_SUFFIX = ".port";
private String user;
private String host;
private String pass;
private int port;
private OmeroLoginDialog dialog;
private AbstractDataSet<DatasetData> currentDatasets;
private AbstractDataSet<ImageData> currentImages;
OmeroBrowser omero;
/**
* Create all instances of UI and API.
*/
public OmeroClient_() {
downloadPrefs();
dialog = new OmeroLoginDialog(this);
dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
dialog.setVisible(true);
currentDatasets = new AbstractDataSet<>();
currentImages = new AbstractDataSet<>();
}
boolean connect() {
LOGGER.trace("Connecting..");
currentDatasets.clear();
currentImages.clear();
try {
disconnect(); // close previous
omero = new OmeroBrowser(user, pass, host, port);
omero.connect();
LOGGER.info("Connected!");
return true;
} catch (Exception e) {
omero.silentClose();
if (!(e instanceof QuimpException)) {
LOGGER.debug(e.getMessage(), e);
QuimpException.showGuiWithMessage(null, QuimpException.prepareMessage(e, "OmeroClient"));
} else {
QuimpException ex = (QuimpException) e;
ex.setMessageSinkType(SOURCE);
ex.handleException(IJ.getInstance(), "OmeroClient");
}
}
return false;
}
void disconnect() {
LOGGER.trace("Closing..");
if (omero != null) {
omero.silentClose();
}
}
/**
* Get datasets from Omero and prepare data to show in UI.
*
* <p>Updates also internal list of current datasets.
*
* @return List of datasets.
*/
List<DatasetData> getDatasets() {
if (omero != null) {
try {
currentDatasets.clear();
currentDatasets.ds.addAll(omero.listDatasets());
} catch (DSOutOfServiceException | DSAccessException | ExecutionException e) {
LOGGER.debug(e.getMessage(), e);
QuimpException.showGuiWithMessage(IJ.getInstance(),
QuimpException.prepareMessage(e, "OmeroClient"));
}
}
return currentDatasets.ds;
}
/**
* Return images from datasetName in format [name,date].
*
* <p>Output is used to fill {@link OmeroLoginDialog#getTableImagesDownload()}. Updates also
* internal list
* of images for current dataset.
*
* @param datasetIndex index of dataset returned by {@link #getDatasets()}.
* @return images from dataset as list
* @see #getDatasets()
*/
List<ImageData> getImages(int datasetIndex) {
DatasetData dstmp = currentDatasets.ds.get(datasetIndex);
try {
currentImages.clear();
currentImages.ds.addAll(omero.openDataset(dstmp));
} catch (DSOutOfServiceException | DSAccessException | ExecutionException e) {
LOGGER.debug(e.getMessage(), e);
QuimpException.showGuiWithMessage(IJ.getInstance(),
QuimpException.prepareMessage(e, "OmeroClient"));
}
return currentImages.ds;
}
/**
* Upload current preferences to IJ_prefs or clear uploaded keys.
*
* @param clear clear if true, upload otherwise
*/
void uploadPrefs(boolean clear) {
if (clear) {
Prefs.set(PREFS_PREFIX + QuimP.QUIMP_PREFS_SUFFIX + PREFS_USER_SUFFIX, "");
Prefs.set(PREFS_PREFIX + QuimP.QUIMP_PREFS_SUFFIX + PREFS_HOST_SUFFIX, "");
Prefs.set(PREFS_PREFIX + QuimP.QUIMP_PREFS_SUFFIX + PREFS_PASS_SUFFIX, "");
Prefs.set(PREFS_PREFIX + QuimP.QUIMP_PREFS_SUFFIX + PREFS_PORT_SUFFIX, OmeroBrowser.DEF_PORT);
LOGGER.info("Prefs cleared");
} else {
Prefs.set(PREFS_PREFIX + QuimP.QUIMP_PREFS_SUFFIX + PREFS_USER_SUFFIX, user);
Prefs.set(PREFS_PREFIX + QuimP.QUIMP_PREFS_SUFFIX + PREFS_HOST_SUFFIX, host);
Prefs.set(PREFS_PREFIX + QuimP.QUIMP_PREFS_SUFFIX + PREFS_PASS_SUFFIX,
Xor.encrypt(pass, KEY));
Prefs.set(PREFS_PREFIX + QuimP.QUIMP_PREFS_SUFFIX + PREFS_PORT_SUFFIX, port);
LOGGER.debug("Prefs uploaded");
}
}
void downloadPrefs() {
user = Prefs.get(PREFS_PREFIX + QuimP.QUIMP_PREFS_SUFFIX + PREFS_USER_SUFFIX, "");
host = Prefs.get(PREFS_PREFIX + QuimP.QUIMP_PREFS_SUFFIX + PREFS_HOST_SUFFIX, "");
pass = Xor.decrypt(Prefs.get(PREFS_PREFIX + QuimP.QUIMP_PREFS_SUFFIX + PREFS_PASS_SUFFIX, ""),
KEY);
port = (int) Prefs.get(PREFS_PREFIX + QuimP.QUIMP_PREFS_SUFFIX + PREFS_PORT_SUFFIX,
OmeroBrowser.DEF_PORT);
}
/**
* Set user.
*
* @param user the user to set
*/
void setUser(String user) {
LOGGER.debug("Set user: " + user);
this.user = user;
if (dialog.getChckbxStoreCred().isSelected()) {
uploadPrefs(false);
}
}
/**
* Set host.
*
* @param host the host to set
*/
void setHost(String host) {
LOGGER.debug("Set host: " + host);
this.host = host;
if (dialog.getChckbxStoreCred().isSelected()) {
uploadPrefs(false);
}
}
/**
* Set pass.
*
* @param pass the pass to set
*/
void setPass(String pass) {
LOGGER.debug("Set pass: " + pass);
this.pass = pass;
if (dialog.getChckbxStoreCred().isSelected()) {
uploadPrefs(false);
}
}
/**
* Set port.
*
* @param port the port to set
*/
void setPort(String port) {
LOGGER.debug("Set port: " + port);
if (!port.isEmpty()) {
this.port = Integer.parseInt(port);
}
if (dialog.getChckbxStoreCred().isSelected()) {
uploadPrefs(false);
}
}
/**
* Get user field.
*
* @return the user
*/
String getUser() {
return user;
}
/**
* Get host field.
*
* @return the host
*/
String getHost() {
return host;
}
/**
* Get pass field.
*
* @return the pass
*/
String getPass() {
return pass;
}
/**
* Get port field.
*
* @return the port
*/
String getPort() {
return String.valueOf(port);
}
/**
* Get id of selected dataset.
*
* @return the currentDs
*/
AbstractDataSet<DatasetData> getCurrentDatasets() {
return currentDatasets;
}
/**
* Set id of selected dataset.
*
* <p>This is index of dataset returned by {@link #getDatasets()}.
*
* @param currentDs the currentDs to set
*/
void setCurrentDs(int currentDs) {
this.currentDatasets.currentEl = currentDs;
}
/**
* Get d of selected image.
*
* @return the currentIm
*/
AbstractDataSet<ImageData> getCurrentImages() {
return currentImages;
}
/**
* Set id of selected image.
*
* <p>This is index of image returned by {@link #getCurrentImages()}.
*
* @param currentIm the currentIm to set
*/
void setCurrentIm(int currentIm) {
this.currentImages.currentEl = currentIm;
}
/**
* Simple XOR implementation to obfuscate password in IJ_Prefs.
*
* @author p.baniukiewicz
*
*/
static class Xor {
/**
* Encrypt string.
*
* @param text string to encrypt
* @param key password
* @return base64 encrypted string
*/
public static String encrypt(String text, String key) {
Encoder enc = java.util.Base64.getEncoder();
return enc.encodeToString(xor(text.getBytes(), key.getBytes()));
}
/**
* Decrypt string.
*
* @param text string base64 to decrypt
* @param key password
* @return decrypted string
*/
public static String decrypt(String text, String key) {
Decoder d = java.util.Base64.getDecoder();
return new String(xor(d.decode(text), key.getBytes()));
}
private static byte[] xor(byte[] text, byte[] key) {
byte[] ret = new byte[text.length];
for (int i = 0; i < text.length; i++) {
ret[i] = (byte) (text[i] ^ key[i % key.length]);
}
return ret;
}
}
/**
* Downloads image.
*
* <p>Image selected by {@link OmeroClient_#setCurrentDs(int)} and then
* {@link OmeroClient_#setCurrentIm(int)}
*
* @param destFolder destination folder
*/
public void download(String destFolder) {
if (currentDatasets.validate() && currentImages.validate()) {
LOGGER.debug("Download: " + currentDatasets.toString() + ", " + currentImages.toString());
try {
omero.download(currentImages.getCurrent(), Paths.get(destFolder));
} catch (ServerError | PermissionDeniedException | CannotCreateSessionException
| DSOutOfServiceException | DSAccessException | ExecutionException | IOException
| URISyntaxException e) {
LOGGER.debug(e.getMessage(), e);
QuimpException.showGuiWithMessage(null, QuimpException.prepareMessage(e, "OmeroClient"));
}
} else {
QuimpException.showGuiWithMessage(null,
"Connect to database first and then select dataset on left and image "
+ "with attached QCONF on right panel.");
}
}
/**
* Upload qconf and related image to currently selected dataset.
*
* @see #setCurrentDs(int)
*/
public void upload() {
if (currentDatasets.validate()) {
try {
QconfLoader qconfLoader = new QconfLoader(null, FileExtensions.newConfigFileExt);
if (qconfLoader.isFileLoaded() == QconfLoader.QCONF_INVALID) {
return;
}
Path qconfPath = qconfLoader.getQconfFile();
qconfLoader.getImage(); // try to read image from qconf and ask to point if abs path wrong
Path imagePath = qconfLoader.getBOA().boap.getOrgFile().toPath();
LOGGER.debug("Upload " + qconfPath.toString() + ", " + imagePath.toString());
if (omero != null && currentDatasets.validate() && imagePath.getFileName() != null) {
omero.upload(new String[] { imagePath.toString() }, currentDatasets.getCurrent());
omero.upload(imagePath.getFileName().toString(), qconfPath.toString(),
currentDatasets.getCurrent());
}
} catch (QuimpException e) {
e.setMessageSinkType(SOURCE);
e.handleException(IJ.getInstance(), "OmeroClient");
} catch (Exception e) {
LOGGER.debug(e.getMessage(), e);
QuimpException.showGuiWithMessage(null, QuimpException.prepareMessage(e, "OmeroClient"));
}
} else {
QuimpException.showGuiWithMessage(null,
"Connect to database first and then select dataset on left panel.");
}
}
}