From cb8abe1f6b83d5809b638a1447f48ea3b38afc90 Mon Sep 17 00:00:00 2001 From: felmer <felmer> Date: Tue, 23 Nov 2010 13:14:40 +0000 Subject: [PATCH] LMS-1888 API extended SVN: 18876 --- .../server/DssServiceRpcScreening.java | 42 +++- .../server/DssServiceRpcScreeningLogger.java | 16 ++ .../api/v1/IDssServiceRpcScreening.java | 40 ++- .../client/api/v1/IPlateImageHandler.java | 32 +++ .../v1/IScreeningOpenbisServiceFacade.java | 13 + .../api/v1/ScreeningClientApiTester.java | 233 ++++++++++++++++++ .../api/v1/ScreeningOpenbisServiceFacade.java | 29 +++ 7 files changed, 392 insertions(+), 13 deletions(-) create mode 100644 screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/api/v1/IPlateImageHandler.java create mode 100644 screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/api/v1/ScreeningClientApiTester.java diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/screening/server/DssServiceRpcScreening.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/screening/server/DssServiceRpcScreening.java index 4f3ccaa2a10..b2f6dc823d4 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/screening/server/DssServiceRpcScreening.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/screening/server/DssServiceRpcScreening.java @@ -340,6 +340,19 @@ public class DssServiceRpcScreening extends AbstractDssServiceRpc<IDssServiceRpc public InputStream loadImages(String sessionToken, List<PlateImageReference> imageReferences, boolean convertToPng) + { + Size thumbnailSizeOrNull = null; + return loadImages(sessionToken, imageReferences, thumbnailSizeOrNull, convertToPng); + } + + public InputStream loadImages(String sessionToken, List<PlateImageReference> imageReferences, + ImageSize thumbnailSizeOrNull) + { + return loadImages(sessionToken, imageReferences, convertToSize(thumbnailSizeOrNull), true); + } + + public InputStream loadImages(String sessionToken, List<PlateImageReference> imageReferences, + Size sizeOrNull, boolean convertToPng) { checkDatasetsAuthorizationForIDatasetIdentifier(sessionToken, imageReferences); final Map<String, IHCSImageDatasetLoader> imageLoadersMap = @@ -351,12 +364,13 @@ public class DssServiceRpcScreening extends AbstractDssServiceRpc<IDssServiceRpc imageLoadersMap.get(imageReference.getDatasetCode()); assert imageAccessor != null : "imageAccessor not found for: " + imageReference; IContent content = - tryGetImageContent(imageAccessor, imageReference, null, convertToPng); + tryGetImageContent(imageAccessor, imageReference, sizeOrNull, convertToPng); imageContents.add(content); } return new ConcatenatedContentInputStream(true, imageContents); } + public InputStream loadImages(String sessionToken, List<PlateImageReference> imageReferences) { return loadImages(sessionToken, imageReferences, true); @@ -370,11 +384,7 @@ public class DssServiceRpcScreening extends AbstractDssServiceRpc<IDssServiceRpc IHCSImageDatasetLoader imageAccessor = createImageLoader(datasetCode, rootDir); List<PlateImageReference> imageReferences = createPlateImageReferences(imageAccessor, dataSetIdentifier, wellPositions, channel); - Size size = null; - if (thumbnailSizeOrNull != null) - { - size = new Size(thumbnailSizeOrNull.getWidth(), thumbnailSizeOrNull.getHeight()); - } + Size size = convertToSize(thumbnailSizeOrNull); List<IContent> imageContents = new ArrayList<IContent>(); for (PlateImageReference imageReference : imageReferences) { @@ -384,6 +394,15 @@ public class DssServiceRpcScreening extends AbstractDssServiceRpc<IDssServiceRpc return new ConcatenatedContentInputStream(true, imageContents); } + public List<PlateImageReference> listPlateImageReferences(String sessionToken, + IDatasetIdentifier dataSetIdentifier, List<WellPosition> wellPositions, String channel) + { + String datasetCode = dataSetIdentifier.getDatasetCode(); + File rootDir = getRootDirectoryForDataSet(datasetCode); + IHCSImageDatasetLoader imageAccessor = createImageLoader(datasetCode, rootDir); + return createPlateImageReferences(imageAccessor, dataSetIdentifier, wellPositions, channel); + } + public void saveImageTransformerFactory(String sessionToken, List<IDatasetIdentifier> dataSetIdentifiers, String channel, IImageTransformerFactory transformerFactory) @@ -555,7 +574,16 @@ public class DssServiceRpcScreening extends AbstractDssServiceRpc<IDssServiceRpc { return new Location(wellPosition.getWellColumn(), wellPosition.getWellRow()); } - + + private Size convertToSize(ImageSize thumbnailSizeOrNull) + { + if (thumbnailSizeOrNull == null) + { + return null; + } + return new Size(thumbnailSizeOrNull.getWidth(), thumbnailSizeOrNull.getHeight()); + } + private IHCSImageDatasetLoader createImageLoader(String datasetCode) { File datasetRoot = getRootDirectoryForDataSet(datasetCode); diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/screening/server/DssServiceRpcScreeningLogger.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/screening/server/DssServiceRpcScreeningLogger.java index 01a82367e4b..4feeeafe408 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/screening/server/DssServiceRpcScreeningLogger.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/screening/server/DssServiceRpcScreeningLogger.java @@ -100,6 +100,14 @@ public class DssServiceRpcScreeningLogger extends AbstractServerLogger implement return null; } + public InputStream loadImages(String sessionToken, List<PlateImageReference> imageReferences, + ImageSize thumbnailSizeOrNull) + { + logAccess(sessionToken, "load_images", "IMAGE_REFERENCES(%s) SIZE(%s)", imageReferences, + thumbnailSizeOrNull); + return null; + } + public InputStream loadImages(String sessionToken, List<PlateImageReference> imageReferences) { logAccess(sessionToken, "load_images", "IMAGE_REFERENCES(%s)", imageReferences); @@ -114,6 +122,14 @@ public class DssServiceRpcScreeningLogger extends AbstractServerLogger implement return null; } + public List<PlateImageReference> listPlateImageReferences(String sessionToken, + IDatasetIdentifier dataSetIdentifier, List<WellPosition> wellPositions, String channel) + { + logAccess(sessionToken, "list_plate_image_references", "DATA_SET(%s) CHANNEL(%s)", + dataSetIdentifier, channel); + return null; + } + public void saveImageTransformerFactory(String sessionToken, List<IDatasetIdentifier> dataSetIdentifiers, String channel, IImageTransformerFactory transformerFactory) diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/screening/shared/api/v1/IDssServiceRpcScreening.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/screening/shared/api/v1/IDssServiceRpcScreening.java index 5c8e86ba0f4..d3d24e918db 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/screening/shared/api/v1/IDssServiceRpcScreening.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/screening/shared/api/v1/IDssServiceRpcScreening.java @@ -133,6 +133,24 @@ public interface IDssServiceRpcScreening extends IRpcService @AuthorizationGuard(guardClass = DatasetIdentifierPredicate.class) List<PlateImageReference> imageReferences, boolean convertToPng); + /** + * Provide images (PNG encoded) for a given list of image references (given by data set code, + * well position, channel and tile). The result is encoded into one stream, which consist of + * multiple blocks in a format: (<block-size><block-of-bytes>)*, where block-size is the block + * size in bytes encoded as one long number. The number of blocks is equal to the number of + * specified references and the order of blocks corresponds to the order of image references. If + * <code>size</code> is specified, the images will be scaled conserving aspect ratio in order to + * fit into specified size. Otherwise images of original size are delivered. + * + * @since 1.4 + */ + @MinimalMinorVersion(4) + @DataSetAccessGuard + public InputStream loadImages( + String sessionToken, + @AuthorizationGuard(guardClass = DatasetIdentifierPredicate.class) List<PlateImageReference> imageReferences, + ImageSize size); + /** * Provide images for a given list of image references (given by data set code, well position, * channel and tile). The result is encoded into one stream, which consist of multiple blocks in @@ -147,11 +165,10 @@ public interface IDssServiceRpcScreening extends IRpcService @AuthorizationGuard(guardClass = DatasetIdentifierPredicate.class) List<PlateImageReference> imageReferences); /** - * Provide images for a specified data set, a list of well positions (empty list means all - * wells), a channel, and an optional thumb nail size. Images of all tiles are delivered. If - * thumb nail size isn't specified the original image is delivered otherwise a thumb nail image - * with same aspect ratio as the original image but which fits into specified size will be - * delivered. + * Provide images for specified data set, list of well positions (empty list means all wells), + * channel, and optional thumb nail size. Images of all tiles are delivered. If thumb nail size + * isn't specified the original image is delivered otherwise a thumb nail image with same aspect + * ratio as the original image but which fits into specified size will be delivered. * <p> * The result is encoded into one stream, which consist of multiple blocks in a format: * (<block-size><block-of-bytes>)*, where block-size is the block size in bytes encoded as one @@ -167,7 +184,18 @@ public interface IDssServiceRpcScreening extends IRpcService String sessionToken, @AuthorizationGuard(guardClass = SingleDataSetIdentifierPredicate.class) IDatasetIdentifier dataSetIdentifier, List<WellPosition> wellPositions, String channel, ImageSize thumbnailSizeOrNull); - + + /** + * Lists plate image references for specified data set, list of well positions (empty list means + * all wells), and channel. + */ + @MinimalMinorVersion(4) + @DataSetAccessGuard + public List<PlateImageReference> listPlateImageReferences( + String sessionToken, + @AuthorizationGuard(guardClass = SingleDataSetIdentifierPredicate.class) IDatasetIdentifier dataSetIdentifier, + List<WellPosition> wellPositions, String channel); + /** * Saves the specified transformer factory for the specified channel and the experiment to * which the specified data sets belong. diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/api/v1/IPlateImageHandler.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/api/v1/IPlateImageHandler.java new file mode 100644 index 00000000000..6bd36360dac --- /dev/null +++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/api/v1/IPlateImageHandler.java @@ -0,0 +1,32 @@ +/* + * Copyright 2010 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.openbis.plugin.screening.client.api.v1; + +import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateImageReference; + +/** + * Interface of classes handling bytes PNG-encoded plate images. + * + * @author Franz-Josef Elmer + */ +public interface IPlateImageHandler +{ + /** + * Handles specified image file bytes for specified plate image reference. + */ + public void handlePlateImage(PlateImageReference plateImageReference, byte[] imageFileBytes); +} diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/api/v1/IScreeningOpenbisServiceFacade.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/api/v1/IScreeningOpenbisServiceFacade.java index 8731c64f3fe..0bbd3813377 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/api/v1/IScreeningOpenbisServiceFacade.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/api/v1/IScreeningOpenbisServiceFacade.java @@ -265,6 +265,19 @@ public interface IScreeningOpenbisServiceFacade List<WellPosition> wellPositions, String channel, ImageSize thumbnailSizeOrNull) throws IOException; + /** + * Loads PNG-encoded images for specified data set, list of well positions (empty + * list means all wells), channel, and optional thumb nail size. Images of all tiles are + * delivered. If thumb nail size isn't specified the original image is delivered otherwise a + * thumb nail image with same aspect ratio as the original image but which fits into specified + * size will be delivered. + * + * @param plateImageHandler Handles delivered images. + */ + public void loadImages(IDatasetIdentifier datasetIdentifier, List<WellPosition> wellPositions, + String channel, ImageSize thumbnailSizeOrNull, IPlateImageHandler plateImageHandler) + throws IOException; + /** * Saves the specified transformer factory for the specified channel and the experiment to * which the specified data sets belong. diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/api/v1/ScreeningClientApiTester.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/api/v1/ScreeningClientApiTester.java new file mode 100644 index 00000000000..27c83bac4b9 --- /dev/null +++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/api/v1/ScreeningClientApiTester.java @@ -0,0 +1,233 @@ +/* + * Copyright 2010 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.openbis.plugin.screening.client.api.v1; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.IOException; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.Properties; + +import javax.swing.BoxLayout; +import javax.swing.ImageIcon; +import javax.swing.JComponent; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JScrollPane; +import javax.swing.JTextField; +import javax.swing.SwingConstants; +import javax.swing.text.JTextComponent; + +import org.apache.log4j.PropertyConfigurator; + +import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.IDatasetIdentifier; +import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.ImageSize; +import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateImageReference; +import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.WellPosition; + +/** + * A test class which shows how to use API. + * + * @author Tomasz Pylak + */ +public class ScreeningClientApiTester +{ + private static final class Form extends JPanel + { + private final JPanel panel; + private final Component parent; + private final String title; + + Form(Component parent, String title) + { + super(new BorderLayout()); + this.parent = parent; + this.title = title; + panel = new JPanel(); + panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); + add(panel, BorderLayout.CENTER); + } + + JTextComponent createTextField(String fieldName, int width, boolean passwordField) + { + JTextComponent result = passwordField ? new JPasswordField(width) : new JTextField(width); + addField(fieldName, result); + return result; + } + + void addField(String fieldName, JComponent field) + { + JPanel fieldPanel = new JPanel(new BorderLayout()); + JLabel label = new JLabel(fieldName + ":"); + Dimension preferredSize = label.getPreferredSize(); + label.setPreferredSize(new Dimension(100, preferredSize.height)); + fieldPanel.add(label, BorderLayout.WEST); + fieldPanel.add(field, BorderLayout.CENTER); + panel.add(fieldPanel); + } + + void showForm() + { + JOptionPane.showMessageDialog(parent, this, title, JOptionPane.QUESTION_MESSAGE); + } + + } + + private static final class TesterFrame extends JFrame + { + private static final long serialVersionUID = 1L; + + private IScreeningOpenbisServiceFacade facade; + + private JPanel content; + + TesterFrame() + { + setTitle("Screening API Tester"); + setSize(800, 600); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + Container contentPane = getContentPane(); + + content = new JPanel(); + content.setLayout(new BorderLayout()); + JScrollPane scrollPane = new JScrollPane(content); + contentPane.add(scrollPane); + + JMenuBar menuBar = new JMenuBar(); + setJMenuBar(menuBar); + JMenu callApiMenu = new JMenu("Call API"); + menuBar.add(callApiMenu); + JMenuItem loadImagesByDataSetMenu = new JMenuItem("Load images by data set code"); + callApiMenu.add(loadImagesByDataSetMenu); + loadImagesByDataSetMenu.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + loadImagesByDataSetCode(); + } + }); + } + + void setUp(String[] args) + { + setVisible(true); + if (args.length == 3) + { + facade = ScreeningOpenbisServiceFacadeFactory.tryCreate(args[0], args[1], args[2]); + } else + { + Form form = new Form(this, "Connection to openBIS"); + JTextField url = new JTextField(20); + JTextField user = new JTextField(20); + JTextField password = new JPasswordField(20); + form.addField("Base URL", url); + form.addField("User ID", user); + form.addField("Password", password); + form.showForm(); + facade = ScreeningOpenbisServiceFacadeFactory.tryCreate(user.getText(), user.getText(), url.getText()); + if (facade == null) + { + throw new RuntimeException("Couldn't connect openBIS."); + } + JOptionPane.showMessageDialog(this, "Successfully connected to openBIS."); + } + } + + private void loadImagesByDataSetCode() + { + Form form = new Form(this, "Parameters for Loading Images by Data Set"); + JTextComponent dataSetField = form.createTextField("Data Set", 20, false); + JTextComponent wellsField = form.createTextField("Wells", 20, false); + JTextComponent channelField = form.createTextField("Channel", 20, false); + form.showForm(); + String dataSetCode = dataSetField.getText(); + List<IDatasetIdentifier> datasetIdentifiers = facade.getDatasetIdentifiers(Arrays.asList(dataSetCode)); + if (datasetIdentifiers.isEmpty()) + { + JOptionPane.showMessageDialog(this, "Unkown data set: " + dataSetCode); + } + List<WellPosition> wellPositions = WellPosition.parseWellPositions(wellsField.getText()); + try + { + content.removeAll(); + final JPanel imagePanel = new JPanel(); + imagePanel.setLayout(new BoxLayout(imagePanel, BoxLayout.Y_AXIS)); + content.add(imagePanel, BorderLayout.CENTER); + facade.loadImages(datasetIdentifiers.get(0), wellPositions, channelField.getText(), new ImageSize( + 200, 100), new IPlateImageHandler() + { + public void handlePlateImage(PlateImageReference plateImageReference, + byte[] imageFileBytes) + { + System.out.println(new Date() + " handle " + plateImageReference); + JLabel image = + new JLabel(plateImageReference.toString(), new ImageIcon( + imageFileBytes), SwingConstants.LEFT); + imagePanel.add(image); + validate(imagePanel); + } + }); + } catch (Exception ex) + { + ex.printStackTrace(); + JOptionPane.showMessageDialog(this, ex.toString()); + } + } + + private void validate(JComponent component) + { + component.invalidate(); + getContentPane().validate(); + } + } + + public static void main(String[] args) throws IOException + { + configureLogging(); + TesterFrame testerFrame = new TesterFrame(); + try + { + testerFrame.setUp(args); + } catch (Throwable ex) + { + ex.printStackTrace(); + JOptionPane.showMessageDialog(testerFrame, ex.toString()); + } + } + + private static void configureLogging() + { + Properties props = new Properties(); + props.put("log4j.appender.STDOUT", "org.apache.log4j.ConsoleAppender"); + props.put("log4j.appender.STDOUT.layout", "org.apache.log4j.PatternLayout"); + props.put("log4j.appender.STDOUT.layout.ConversionPattern", "%d %-5p [%t] %c - %m%n"); + props.put("log4j.rootLogger", "INFO, STDOUT"); + PropertyConfigurator.configure(props); + } +} diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/api/v1/ScreeningOpenbisServiceFacade.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/api/v1/ScreeningOpenbisServiceFacade.java index 41c71321b47..b9923dc9355 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/api/v1/ScreeningOpenbisServiceFacade.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/api/v1/ScreeningOpenbisServiceFacade.java @@ -566,6 +566,35 @@ public class ScreeningOpenbisServiceFacade implements IScreeningOpenbisServiceFa return result; } + public void loadImages(IDatasetIdentifier dataSetIdentifier, List<WellPosition> wellPositions, + String channel, ImageSize thumbnailSizeOrNull, IPlateImageHandler plateImageHandler) + throws IOException + { + DssServiceRpcScreeningHolder dssServiceHolder = + dssServiceCache.createDssService(dataSetIdentifier.getDatastoreServerUrl()); + IDssServiceRpcScreening service = dssServiceHolder.getService(); + List<PlateImageReference> plateImageReferences = + service.listPlateImageReferences(sessionToken, dataSetIdentifier, wellPositions, + channel); + InputStream stream = + service.loadImages(sessionToken, plateImageReferences, thumbnailSizeOrNull); + ConcatenatedFileOutputStreamWriter imagesWriter = + new ConcatenatedFileOutputStreamWriter(stream); + int index = 0; + long size; + do + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + size = imagesWriter.writeNextBlock(outputStream); + if (size > 0) + { + plateImageHandler.handlePlateImage(plateImageReferences.get(index), + outputStream.toByteArray()); + } + index++; + } while (size >= 0); + } + public void saveImageTransformerFactory(List<IDatasetIdentifier> dataSetIdentifiers, String channel, IImageTransformerFactory transformerFactory) { -- GitLab