/*
*------------------------------------------------------------------------------
* Copyright (C) 2015-2016 University of Dundee. All rights reserved.
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*------------------------------------------------------------------------------
*/
package omero.gateway.facility;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import omero.ServerError;
import omero.api.IContainerPrx;
import omero.api.IUpdatePrx;
import omero.cmd.CmdCallbackI;
import omero.api.RawFileStorePrx;
import omero.cmd.Request;
import omero.cmd.Response;
import omero.gateway.Gateway;
import omero.gateway.SecurityContext;
import omero.gateway.exception.DSAccessException;
import omero.gateway.exception.DSOutOfServiceException;
import omero.gateway.model.DataObject;
import omero.gateway.model.DatasetData;
import omero.gateway.model.ImageData;
import omero.gateway.util.PojoMapper;
import omero.gateway.util.Requests;
import omero.model.ChecksumAlgorithm;
import omero.model.ChecksumAlgorithmI;
import omero.model.DatasetAnnotationLink;
import omero.model.DatasetAnnotationLinkI;
import omero.model.DatasetImageLink;
import omero.model.DatasetImageLinkI;
import omero.model.FileAnnotation;
import omero.model.FileAnnotationI;
import omero.model.IObject;
import omero.model.ImageAnnotationLink;
import omero.model.ImageAnnotationLinkI;
import omero.model.OriginalFile;
import omero.model.OriginalFileI;
import omero.model.PlateAnnotationLink;
import omero.model.PlateAnnotationLinkI;
import omero.model.ProjectAnnotationLink;
import omero.model.ProjectAnnotationLinkI;
import omero.model.ProjectDatasetLink;
import omero.model.ProjectDatasetLinkI;
import omero.model.ScreenAnnotationLink;
import omero.model.ScreenAnnotationLinkI;
import omero.model.WellAnnotationLink;
import omero.model.WellAnnotationLinkI;
import omero.model.enums.ChecksumAlgorithmSHA1160;
import omero.sys.Parameters;
import omero.gateway.model.AnnotationData;
import omero.gateway.model.FileAnnotationData;
import omero.gateway.model.PlateData;
import omero.gateway.model.ProjectData;
import omero.gateway.model.ScreenData;
import omero.gateway.model.WellData;
import omero.gateway.model.WellSampleData;
/**
* A {@link Facility} for saving, deleting and updating data objects
*
* @author Dominik Lindner <a
* href="mailto:d.lindner@dundee.ac.uk">d.lindner@dundee.ac.uk</a>
* @since 5.1
*/
public class DataManagerFacility extends Facility {
/** Reference to the {@link BrowseFacility} */
private BrowseFacility browse;
/** Default file upload buffer size */
private int INC = 262144;
/**
* Creates a new instance
*
* @param gateway
* Reference to the {@link Gateway}
*/
DataManagerFacility(Gateway gateway) throws ExecutionException {
super(gateway);
this.browse = gateway.getFacility(BrowseFacility.class);
}
/**
* Deletes the specified object.
*
* @deprecated Use the asynchronous method
* {@link #delete(SecurityContext, IObject)} instead
* @param ctx
* The security context.
* @param object
* The object to delete.
* @return The {@link Response} handle
* @throws DSOutOfServiceException
* If the connection is broken, or not logged in
* @throws DSAccessException
* If an error occurred while trying to retrieve data from OMERO
* service.
*/
public Response deleteObject(SecurityContext ctx, IObject object)
throws DSOutOfServiceException, DSAccessException {
return deleteObjects(ctx, Collections.singletonList(object));
}
/**
* Deletes the specified object asynchronously
*
* @param ctx
* The security context.
* @param object
* The object to delete.
* @return The {@link CmdCallbackI}
* @throws DSOutOfServiceException
* If the connection is broken, or not logged in
* @throws DSAccessException
* If an error occurred while trying to retrieve data from OMERO
* service.
*/
public CmdCallbackI delete(SecurityContext ctx, IObject object)
throws DSOutOfServiceException, DSAccessException {
return delete(ctx, Collections.singletonList(object));
}
/**
* Deletes the specified objects asynchronously
*
* @param ctx
* The security context.
* @param objects
* The objects to delete.
* @return The {@link CmdCallbackI}
* @throws DSOutOfServiceException
* If the connection is broken, or logged in
* @throws DSAccessException
* If an error occurred while trying to retrieve data from OMERO
* service.
*/
public CmdCallbackI delete(SecurityContext ctx, List<IObject> objects)
throws DSOutOfServiceException, DSAccessException {
try {
/*
* convert the list of objects to lists of IDs by OMERO model class
* name
*/
final Map<String, List<Long>> objectIds = new HashMap<String, List<Long>>();
for (final IObject object : objects) {
/* determine actual model class name for this object */
Class<? extends IObject> objectClass = object.getClass();
while (true) {
final Class<?> superclass = objectClass.getSuperclass();
if (IObject.class == superclass) {
break;
} else {
objectClass = superclass.asSubclass(IObject.class);
}
}
final String objectClassName = objectClass.getSimpleName();
/* then add the object's ID to the list for that class name */
final Long objectId = object.getId().getValue();
List<Long> idsThisClass = objectIds.get(objectClassName);
if (idsThisClass == null) {
idsThisClass = new ArrayList<Long>();
objectIds.put(objectClassName, idsThisClass);
}
idsThisClass.add(objectId);
}
/* now delete the objects */
final Request request = Requests.delete(objectIds);
return gateway.submit(ctx, request);
} catch (Throwable t) {
handleException(this, t, "Cannot delete the object.");
}
return null;
}
/**
* Deletes the specified objects.
*
* @deprecated Use the asynchronous method
* {@link #delete(SecurityContext, List)} instead
*
* @param ctx
* The security context.
* @param objects
* The objects to delete.
* @return The {@link Response} handle
* @throws DSOutOfServiceException
* If the connection is broken, or not logged in
* @throws DSAccessException
* If an error occurred while trying to retrieve data from OMERO
* service.
*/
public Response deleteObjects(SecurityContext ctx, List<IObject> objects)
throws DSOutOfServiceException, DSAccessException {
try {
/*
* convert the list of objects to lists of IDs by OMERO model class
* name
*/
final Map<String, List<Long>> objectIds = new HashMap<String, List<Long>>();
for (final IObject object : objects) {
/* determine actual model class name for this object */
Class<? extends IObject> objectClass = object.getClass();
while (true) {
final Class<?> superclass = objectClass.getSuperclass();
if (IObject.class == superclass) {
break;
} else {
objectClass = superclass.asSubclass(IObject.class);
}
}
final String objectClassName = objectClass.getSimpleName();
/* then add the object's ID to the list for that class name */
final Long objectId = object.getId().getValue();
List<Long> idsThisClass = objectIds.get(objectClassName);
if (idsThisClass == null) {
idsThisClass = new ArrayList<Long>();
objectIds.put(objectClassName, idsThisClass);
}
idsThisClass.add(objectId);
}
/* now delete the objects */
final Request request = Requests.delete(objectIds);
return gateway.submit(ctx, request).loop(50, 250);
} catch (Throwable t) {
handleException(this, t, "Cannot delete the object.");
}
return null;
}
/**
* Updates the specified object.
*
* @param ctx
* The security context.
* @param object
* The object to update.
* @param options
* Options to update the data.
* @return The updated object.
* @throws DSOutOfServiceException
* If the connection is broken, or not logged in
* @throws DSAccessException
* If an error occurred while trying to retrieve data from OMERO
* service.
* @see IContainerPrx#updateDataObject(IObject, Parameters)
*/
public IObject saveAndReturnObject(SecurityContext ctx, IObject object,
Map options) throws DSOutOfServiceException, DSAccessException {
try {
IUpdatePrx service = gateway.getUpdateService(ctx);
if (options == null)
return service.saveAndReturnObject(object);
return service.saveAndReturnObject(object, options);
} catch (Throwable t) {
handleException(this, t, "Cannot update the object.");
}
return null;
}
/**
* Updates the specified object.
*
* @param ctx
* The security context.
* @param object
* The object to update.
* @return The updated object.
* @throws DSOutOfServiceException
* If the connection is broken, or not logged in
* @throws DSAccessException
* If an error occurred while trying to retrieve data from OMERO
* service.
* @see IContainerPrx#updateDataObject(IObject, Parameters)
*/
public DataObject saveAndReturnObject(SecurityContext ctx, DataObject object)
throws DSOutOfServiceException, DSAccessException {
return PojoMapper.asDataObject(saveAndReturnObject(ctx,
object.asIObject()));
}
/**
* Updates the specified object.
*
* @param ctx
* The security context.
* @param object
* The object to update.
* @return The updated object.
* @throws DSOutOfServiceException
* If the connection is broken, or not logged in
* @throws DSAccessException
* If an error occurred while trying to retrieve data from OMERO
* service.
* @see IContainerPrx#updateDataObject(IObject, Parameters)
*/
public IObject saveAndReturnObject(SecurityContext ctx, IObject object)
throws DSOutOfServiceException, DSAccessException {
try {
IUpdatePrx service = gateway.getUpdateService(ctx);
IObject result = service.saveAndReturnObject(object);
return result;
} catch (Throwable t) {
handleException(this, t, "Cannot update the object.");
}
return null;
}
/**
* Updates the specified object.
*
* @param ctx
* The security context.
* @param object
* The object to update.
* @param options
* Options to update the data.
* @param userName
* The name of the user to create the data for.
* @return The updated object.
* @throws DSOutOfServiceException
* If the connection is broken, or not logged in
* @throws DSAccessException
* If an error occurred while trying to retrieve data from OMERO
* service.
* @see IContainerPrx#updateDataObject(IObject, Parameters)
*/
public IObject saveAndReturnObject(SecurityContext ctx, IObject object,
Map options, String userName) throws DSOutOfServiceException,
DSAccessException {
try {
IUpdatePrx service = gateway.getUpdateService(ctx, userName);
if (options == null)
return service.saveAndReturnObject(object);
return service.saveAndReturnObject(object, options);
} catch (Throwable t) {
handleException(this, t, "Cannot update the object.");
}
return null;
}
/**
* Updates the specified object.
*
* @param ctx
* The security context.
* @param object
* The object to update.
* @param userName
* The name of the user to create the data for.
* @return The updated object.
* @throws DSOutOfServiceException
* If the connection is broken, or not logged in
* @throws DSAccessException
* If an error occurred while trying to retrieve data from OMERO
* service.
* @see IContainerPrx#updateDataObject(IObject, Parameters)
*/
public DataObject saveAndReturnObject(SecurityContext ctx,
DataObject object, String userName) throws DSOutOfServiceException,
DSAccessException {
return PojoMapper.asDataObject(saveAndReturnObject(ctx,
object.asIObject(), userName));
}
/**
* Updates the specified object.
*
* @param ctx
* The security context.
* @param object
* The object to update.
* @param userName
* The name of the user to create the data for.
* @return The updated object.
* @throws DSOutOfServiceException
* If the connection is broken, or not logged in
* @throws DSAccessException
* If an error occurred while trying to retrieve data from OMERO
* service.
* @see IContainerPrx#updateDataObject(IObject, Parameters)
*/
public IObject saveAndReturnObject(SecurityContext ctx, IObject object,
String userName) throws DSOutOfServiceException, DSAccessException {
try {
IUpdatePrx service = gateway.getUpdateService(ctx, userName);
IObject result = service.saveAndReturnObject(object);
return result;
} catch (Throwable t) {
handleException(this, t, "Cannot update the object.");
}
return null;
}
/**
* Updates the specified object.
*
* @param ctx
* The security context.
* @param objects
* The objects to update.
* @param options
* Options to update the data.
* @param userName
* The username
* @return The updated object.
* @throws DSOutOfServiceException
* If the connection is broken, or not logged in
* @throws DSAccessException
* If an error occurred while trying to retrieve data from OMERO
* service.
* @see IContainerPrx#updateDataObject(IObject, Parameters)
*/
public List<IObject> saveAndReturnObject(SecurityContext ctx,
List<IObject> objects, Map options, String userName)
throws DSOutOfServiceException, DSAccessException {
try {
IUpdatePrx service = gateway.getUpdateService(ctx, userName);
return service.saveAndReturnArray(objects);
} catch (Throwable t) {
handleException(this, t, "Cannot update the object.");
}
return new ArrayList<IObject>();
}
/**
* Updates the specified object.
*
* @param ctx
* The security context.
* @param object
* The object to update.
* @param options
* Options to update the data.
* @return The updated object.
* @throws DSOutOfServiceException
* If the connection is broken, or not logged in
* @throws DSAccessException
* If an error occurred while trying to retrieve data from OMERO
* service.
* @see IContainerPrx#updateDataObject(IObject, Parameters)
*/
public IObject updateObject(SecurityContext ctx, IObject object,
Parameters options) throws DSOutOfServiceException,
DSAccessException {
try {
IContainerPrx service = gateway.getPojosService(ctx);
IObject r = service.updateDataObject(object, options);
return browse.findIObject(ctx, r);
} catch (Throwable t) {
handleException(this, t, "Cannot update the object.");
}
return null;
}
/**
* Updates the specified <code>IObject</code>s and returned the updated
* <code>IObject</code>s.
*
* @param ctx
* The security context.
* @param objects
* The array of objects to update.
* @param options
* Options to update the data.
* @return See above.
* @throws DSOutOfServiceException
* If the connection is broken, or not logged in
* @throws DSAccessException
* If an error occurred while trying to retrieve data from OMERO
* service.
* @see IContainerPrx#updateDataObjects(List, Parameters)
*/
public List<IObject> updateObjects(SecurityContext ctx,
List<IObject> objects, Parameters options)
throws DSOutOfServiceException, DSAccessException {
try {
IContainerPrx service = gateway.getPojosService(ctx);
List<IObject> l = service.updateDataObjects(objects, options);
if (l == null)
return l;
Iterator<IObject> i = l.iterator();
List<IObject> r = new ArrayList<IObject>(l.size());
IObject io;
while (i.hasNext()) {
io = browse.findIObject(ctx, i.next());
if (io != null)
r.add(io);
}
return r;
} catch (Throwable t) {
handleException(this, t, "Cannot update the object.");
}
return new ArrayList<IObject>();
}
/**
* Adds the {@link ImageData} to the given {@link DatasetData}
*
* @param ctx
* The security context.
* @param image
* The image to add to the dataset
* @param ds
* The dataset to add the image to
* @throws DSOutOfServiceException
* If the connection is broken, or not logged in
* @throws DSAccessException
* If an error occurred while trying to retrieve data from OMERO
* service.
*/
public void addImageToDataset(SecurityContext ctx, ImageData image,
DatasetData ds) throws DSOutOfServiceException, DSAccessException {
List<ImageData> l = new ArrayList<ImageData>(1);
l.add(image);
addImagesToDataset(ctx, l, ds);
}
/**
* Adds the {@link ImageData}s to the given {@link DatasetData}
*
* @param ctx
* The security context.
* @param images
* The images to add to the dataset
* @param ds
* The dataset to add the images to
* @throws DSOutOfServiceException
* If the connection is broken, or not logged in
* @throws DSAccessException
* If an error occurred while trying to retrieve data from OMERO
* service.
*/
public void addImagesToDataset(SecurityContext ctx,
Collection<ImageData> images, DatasetData ds)
throws DSOutOfServiceException, DSAccessException {
List<IObject> links = new ArrayList<IObject>();
for (ImageData img : images) {
DatasetImageLink l = new DatasetImageLinkI();
l.setParent(ds.asDataset());
l.setChild(img.asImage());
links.add(l);
}
updateObjects(ctx, links, null);
}
/**
* Creates the {@link DatasetData} on the server and attaches it
* to the {@link ProjectData} (if not <code>null</code>) (if the
* project doesn't exist on the server yet, it will be created, too)
* @param ctx The {@link SecurityContext}
* @param dataset The {@link DatasetData}
* @param project The {@link ProjectData}
* @return The {@link DatasetData}
* @throws DSOutOfServiceException
* If the connection is broken, or not logged in
* @throws DSAccessException
* If an error occurred while trying to retrieve data from OMERO
* service.
*/
public DatasetData createDataset(SecurityContext ctx, DatasetData dataset,
ProjectData project) throws DSOutOfServiceException,
DSAccessException {
if (project != null) {
ProjectDatasetLink link = new ProjectDatasetLinkI();
link = new ProjectDatasetLinkI();
link.setChild(dataset.asDataset());
link.setParent(project.asProject());
link = (ProjectDatasetLink) saveAndReturnObject(ctx, link);
return new DatasetData(link.getChild());
} else {
return (DatasetData) saveAndReturnObject(ctx, dataset);
}
}
/**
* Attaches a {@link File} to a {@link DataObject}
*
* @param ctx
* The {@link SecurityContext}
* @param file
* The {@link File} to attach
* @param mimetype
* The mimetype of the file (can be <code>null</code>)
* @param description
* A description (can be <code>null</code>)
* @param namespace
* The namespace (can be <code>null</code>)
* @param target
* The {@link DataObject} to attach the file to
* @return The {@link Future} {@link FileAnnotationData}
*/
public Future<FileAnnotationData> attachFile(final SecurityContext ctx,
final File file, String mimetype, final String description,
final String namespace, final DataObject target) {
final String name = file.getName();
String absolutePath = file.getAbsolutePath();
final String path = absolutePath.substring(0, absolutePath.length()
- name.length());
final String mime;
if (mimetype == null) {
try {
mimetype = Files.probeContentType(Paths.get(file.toURI()));
} catch (IOException e) {
mimetype = null;
}
mime = mimetype != null ? mimetype : "application/octet-stream";
} else
mime = mimetype;
Callable<FileAnnotationData> c = new Callable<FileAnnotationData>() {
@Override
public FileAnnotationData call() throws Exception {
RawFileStorePrx rawFileStore = null;
FileInputStream stream = null;
try {
OriginalFile originalFile = new OriginalFileI();
originalFile.setName(omero.rtypes.rstring(name));
originalFile.setPath(omero.rtypes.rstring(path));
originalFile.setSize(omero.rtypes.rlong(file.length()));
final ChecksumAlgorithm checksumAlgorithm = new ChecksumAlgorithmI();
checksumAlgorithm.setValue(omero.rtypes
.rstring(ChecksumAlgorithmSHA1160.value));
originalFile.setHasher(checksumAlgorithm);
originalFile.setMimetype(omero.rtypes.rstring(mime));
originalFile = (OriginalFile) saveAndReturnObject(ctx,
originalFile);
rawFileStore = gateway.getRawFileService(ctx);
rawFileStore.setFileId(originalFile.getId().getValue());
stream = new FileInputStream(file);
long pos = 0;
int rlen;
byte[] buf = new byte[INC];
ByteBuffer bbuf;
while (((rlen = stream.read(buf)) > 0)) {
if (Thread.currentThread().isInterrupted()) {
stream.close();
try {
rawFileStore.close();
} catch (ServerError e) {
}
return null;
}
rawFileStore.write(buf, pos, rlen);
pos += rlen;
bbuf = ByteBuffer.wrap(buf);
bbuf.limit(rlen);
}
originalFile = rawFileStore.save();
FileAnnotation fa = new FileAnnotationI();
fa.setFile(originalFile);
if (description != null)
fa.setDescription(omero.rtypes.rstring(description));
fa.setNs(omero.rtypes.rstring(namespace));
fa = (FileAnnotation) saveAndReturnObject(ctx, fa);
if (target != null)
return attachAnnotation(ctx,
new FileAnnotationData(fa), target);
else
return new FileAnnotationData(fa);
} finally {
if (stream != null)
stream.close();
if (rawFileStore != null) {
try {
rawFileStore.close();
} catch (ServerError e) {
}
}
}
}
};
return gateway.submit(c);
}
/**
* Create/attach an {@link AnnotationData} to a given {@link DataObject}
*
* @param ctx
* The {@link SecurityContext}
* @param annotation
* The {@link AnnotationData}
* @param target
* The {@link DataObject} to attach to
* @return The {@link AnnotationData}
* @throws DSOutOfServiceException
* If the connection is broken, or not logged in
* @throws DSAccessException
* If an error occurred while trying to retrieve data from OMERO
* service.
*/
public <T extends AnnotationData> T attachAnnotation(SecurityContext ctx,
T annotation, DataObject target) throws DSOutOfServiceException,
DSAccessException {
if (target != null) {
if (target instanceof ProjectData) {
ProjectData project = browse.findObject(ctx, ProjectData.class,
target.getId());
ProjectAnnotationLink link = new ProjectAnnotationLinkI();
link.setChild(annotation.asAnnotation());
link.setParent(project.asProject());
link = (ProjectAnnotationLink) saveAndReturnObject(ctx, link);
return (T) PojoMapper.asDataObject(link.getChild());
}
if (target instanceof DatasetData) {
DatasetData ds = browse.findObject(ctx, DatasetData.class,
target.getId());
DatasetAnnotationLink link = new DatasetAnnotationLinkI();
link.setChild(annotation.asAnnotation());
link.setParent(ds.asDataset());
link = (DatasetAnnotationLink) saveAndReturnObject(ctx, link);
return (T) PojoMapper.asDataObject(link.getChild());
}
if (target instanceof ScreenData) {
ScreenData s = browse.findObject(ctx, ScreenData.class,
target.getId());
ScreenAnnotationLink link = new ScreenAnnotationLinkI();
link.setChild(annotation.asAnnotation());
link.setParent(s.asScreen());
link = (ScreenAnnotationLink) saveAndReturnObject(ctx, link);
return (T) PojoMapper.asDataObject(link.getChild());
}
if (target instanceof PlateData) {
PlateData p = browse.findObject(ctx, PlateData.class,
target.getId());
PlateAnnotationLink link = new PlateAnnotationLinkI();
link.setChild(annotation.asAnnotation());
link.setParent(p.asPlate());
link = (PlateAnnotationLink) saveAndReturnObject(ctx, link);
return (T) PojoMapper.asDataObject(link.getChild());
}
if (target instanceof WellData) {
WellData w = browse.findObject(ctx, WellData.class,
target.getId());
WellAnnotationLink link = new WellAnnotationLinkI();
link.setChild(annotation.asAnnotation());
link.setParent(w.asWell());
link = (WellAnnotationLink) saveAndReturnObject(ctx, link);
return (T) PojoMapper.asDataObject(link.getChild());
}
if (target instanceof ImageData) {
ImageData i = browse.findObject(ctx, ImageData.class,
target.getId());
ImageAnnotationLink link = new ImageAnnotationLinkI();
link.setChild(annotation.asAnnotation());
link.setParent(i.asImage());
link = (ImageAnnotationLink) saveAndReturnObject(ctx, link);
return (T) PojoMapper.asDataObject(link.getChild());
}
if (target instanceof WellSampleData) {
WellSampleData w = browse.findObject(ctx, WellSampleData.class,
target.getId());
ImageData i = browse.findObject(ctx, ImageData.class, w
.getImage().getId());
ImageAnnotationLink link = new ImageAnnotationLinkI();
link.setChild(annotation.asAnnotation());
link.setParent(i.asImage());
link = (ImageAnnotationLink) saveAndReturnObject(ctx, link);
return (T) PojoMapper.asDataObject(link.getChild());
}
} else {
return (T) saveAndReturnObject(ctx, annotation);
}
return null;
}
}