qupath / qupath-extension-omero

QuPath extension to work with images through OMERO's APIs
Apache License 2.0
3 stars 1 forks source link

Cannot use ICE client using v0.1.0-rc1 #1

Open lacan opened 8 months ago

lacan commented 8 months ago

When connecting to our OMERO instance using https://omero.epfl.ch, we are able to browse all the files.

We also included the files necessary to have the ICE API being recognized image

Unfortunately upon trying to import a Fluorescent image we receive

Unable to create OMERO server
omero.ClientError: Obtained null object proxy
    at omero.client.createSession(client.java:796)
    at omero.gateway.Gateway.createSession(Gateway.java:1034)
    at omero.gateway.Gateway.connect(Gateway.java:295)
    at qupath.ext.omero.core.pixelapis.ice.IceReader.connect(IceReader.java:159)
    at qupath.ext.omero.core.pixelapis.ice.IceReader.<init>(IceReader.java:58)
    at qupath.ext.omero.core.pixelapis.ice.IceAPI.createReader(IceAPI.java:85)
    at qupath.ext.omero.core.imageserver.OmeroImageServer.create(OmeroImageServer.java:72)
    at qupath.ext.omero.core.imageserver.OmeroImageServerBuilder.lambda$buildServer$0(OmeroImageServerBuilder.java:30)
    at java.base/java.util.Optional.flatMap(Unknown Source)
    at qupath.ext.omero.core.imageserver.OmeroImageServerBuilder.buildServer(OmeroImageServerBuilder.java:30)
    at qupath.ext.omero.core.imageserver.OmeroImageServerBuilder.lambda$checkImageSupport$1(OmeroImageServerBuilder.java:44)
    at java.base/java.util.stream.ReferencePipeline$3$1.accept(Unknown Source)
    at java.base/java.util.AbstractList$RandomAccessSpliterator.forEachRemaining(Unknown Source)
    at java.base/java.util.stream.AbstractPipeline.copyInto(Unknown Source)
    at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(Unknown Source)
    at java.base/java.util.stream.AbstractPipeline.evaluate(Unknown Source)
    at java.base/java.util.stream.AbstractPipeline.evaluateToArrayNode(Unknown Source)
    at java.base/java.util.stream.ReferencePipeline.toArray(Unknown Source)
    at java.base/java.util.stream.ReferencePipeline.toArray(Unknown Source)
    at java.base/java.util.stream.ReferencePipeline.toList(Unknown Source)
    at qupath.ext.omero.core.imageserver.OmeroImageServerBuilder.checkImageSupport(OmeroImageServerBuilder.java:52)
    at qupath.lib.images.servers.ImageServers.getImageSupport(ImageServers.java:387)
    at qupath.lib.images.servers.ImageServers.getImageSupport(ImageServers.java:419)
    at qupath.lib.gui.commands.ProjectImportImagesCommand$1.lambda$call$0(ProjectImportImagesCommand.java:366)
    at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.base/java.lang.Thread.run(Unknown Source)

This is caused by the fact that our IT service separated omero.server (ICE) from omero.web (Web API) by giving them separate aliases and certificates, which is apparently best practice, but means that we cannot use ICE with the extension as-is because the OMERO Extension would need to authenticate to https://omero-server.epfl.ch instead of https://omero.epfl.ch

Because there is no Web API accessible via https://omero-server.epfl.ch, we cannot enter the Extension using that URL, because then we get the error

Could not find API URL in https://omero-server.epfl.ch/api/
Connection to https://omero-server.epfl.ch/api/ failed
java.util.concurrent.CompletionException: java.net.http.HttpConnectTimeoutException: HTTP connect timed out
    at java.base/java.util.concurrent.CompletableFuture.encodeRelay(Unknown Source)
    at java.base/java.util.concurrent.CompletableFuture.completeRelay(Unknown Source)
    at java.base/java.util.concurrent.CompletableFuture$UniCompose.tryFire(Unknown Source)
    at java.base/java.util.concurrent.CompletableFuture.postComplete(Unknown Source)
    at java.base/java.util.concurrent.CompletableFuture.completeExceptionally(Unknown Source)
    at java.net.http/jdk.internal.net.http.Http1Exchange.lambda$cancelImpl$9(Unknown Source)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.base/java.lang.Thread.run(Unknown Source)
Caused by: java.net.http.HttpConnectTimeoutException: HTTP connect timed out
    at java.net.http/jdk.internal.net.http.ResponseTimerEvent.handle(Unknown Source)
    at java.net.http/jdk.internal.net.http.HttpClientImpl.purgeTimeoutsAndReturnNextDeadline(Unknown Source)
    at java.net.http/jdk.internal.net.http.HttpClientImpl$SelectorManager.run(Unknown Source)
Caused by: java.net.ConnectException: HTTP connect timed out
    ... 3 more

So this would mean for us that in the Settings, there should be a Ice API server address that could be set to something else than the Web API one. image

We will test the OMERO Pixel Data Microservice after our IT install it on our OMERO test server and get back to you.

psobolewskiPhD commented 8 months ago

Here at JAX we also have our web and omero servers separated, so I've got this same issue, but without the error--I simply always get web client in the URI when opening an image.

Edit: Actually, it does show Server type to be ICE in QuPath, but I don't think it can be reading the actual pixels. Will explore more.

Rylern commented 7 months ago

Thanks, I created the v0.1.0-rc2 release that should fix the issue. The extension should by itself be able to automatically retrieve the omero.server address when the omero.web address can't be used with the ICE API. If that's still not the case, I added settings to manually set the address and the port of the omero.server.

Can you see if that version fixes the issue?

psobolewskiPhD commented 7 months ago

This isn't quite working yet. It looks like the hostname entered into the Settings isn't being used correctly, just the first part is. You can see it using ctomero and not ctomero.jax.org.

15:45:48.645    [JavaFX Application Thread] INFO    qupath.ext.omero.core.pixelapis.ice.IceReader   Can't connect to ctomero:4064   omero.gateway.exception.DSOutOfServiceException: Can't resolve hostname ctomero
    at omero.gateway.Gateway.connect(Gateway.java:312)
    at qupath.ext.omero.core.pixelapis.ice.IceReader.connect(IceReader.java:150)
    at qupath.ext.omero.core.pixelapis.ice.IceReader.<init>(IceReader.java:57)
    at qupath.ext.omero.core.pixelapis.ice.IceAPI.createReader(IceAPI.java:156)
    at qupath.ext.omero.core.imageserver.OmeroImageServer.create(OmeroImageServer.java:72)
    at qupath.ext.omero.core.imageserver.OmeroImageServerBuilder.lambda$buildServer$0(OmeroImageServerBuilder.java:30)
    at java.base/java.util.Optional.flatMap(Unknown Source)
    at qupath.ext.omero.core.imageserver.OmeroImageServerBuilder.buildServer(OmeroImageServerBuilder.java:30)
    at qupath.lib.images.servers.ImageServerBuilder$DefaultImageServerBuilder.buildOriginal(ImageServerBuilder.java:350)
    at qupath.lib.images.servers.ImageServerBuilder$AbstractServerBuilder.build(ImageServerBuilder.java:174)
    at qupath.lib.gui.QuPathGUI.openImage(QuPathGUI.java:1713)
    at qupath.ext.omero.gui.UiUtilities.openImages(UiUtilities.java:152)
    at qupath.ext.omero.gui.browser.serverbrowser.Browser.onImagesTreeClicked(Browser.java:226)
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Unknown Source)
    at java.base/java.lang.reflect.Method.invoke(Unknown Source)
    at com.sun.javafx.reflect.Trampoline.invoke(MethodUtil.java:72)
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Unknown Source)
    at java.base/java.lang.reflect.Method.invoke(Unknown Source)
    at com.sun.javafx.reflect.MethodUtil.invoke(MethodUtil.java:270)
    at com.sun.javafx.fxml.MethodHelper.invoke(MethodHelper.java:84)
    at javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1853)
    at javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1726)
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:232)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:189)
    at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
    at javafx.event.Event.fireEvent(Event.java:198)
    at javafx.scene.Scene$ClickGenerator.postProcess(Scene.java:3684)
    at javafx.scene.Scene$MouseHandler.process(Scene.java:3989)
    at javafx.scene.Scene.processMouseEvent(Scene.java:1890)
    at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2704)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:411)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:301)
    at java.base/java.security.AccessController.doPrivileged(Unknown Source)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$2(GlassViewEventHandler.java:450)
    at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:424)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:449)
    at com.sun.glass.ui.View.handleMouseEvent(View.java:551)
    at com.sun.glass.ui.View.notifyMouse(View.java:937)
    at com.sun.glass.ui.mac.MacView.notifyMouse(MacView.java:127)
Caused by: Ice.DNSException
    error = 0
    host = "ctomero"
    at IceInternal.AsyncResultI.__wait(AsyncResultI.java:276)
    at Ice.ObjectPrxHelperBase.end_ice_isA(ObjectPrxHelperBase.java:310)
    at Ice.ObjectPrxHelperBase.ice_isA(ObjectPrxHelperBase.java:92)
    at Ice.ObjectPrxHelperBase.ice_isA(ObjectPrxHelperBase.java:69)
    at Ice.ObjectPrxHelperBase.checkedCastImpl(ObjectPrxHelperBase.java:2810)
    at Ice.ObjectPrxHelperBase.checkedCastImpl(ObjectPrxHelperBase.java:2770)
    at Glacier2.RouterPrxHelper.checkedCast(RouterPrxHelper.java:1787)
    at omero.client.getRouter(client.java:848)
    at omero.client.createSession(client.java:769)
    at omero.gateway.Gateway.createSession(Gateway.java:1034)
    at omero.gateway.Gateway.connect(Gateway.java:295)
    ... 49 more
Caused by: java.net.UnknownHostException: ctomero
    at java.base/java.net.InetAddress$CachedLookup.get(Unknown Source)
    at java.base/java.net.InetAddress.getAllByName0(Unknown Source)
    at java.base/java.net.InetAddress.getAllByName(Unknown Source)
    at IceInternal.Network.getAddresses(Network.java:832)
    at IceInternal.EndpointHostResolver$1.run(EndpointHostResolver.java:103)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.base/java.lang.Thread.run(Unknown Source)

Edit: Also that wasn't a red, error in the logs, and the image opened, claiming to use OMERO (Ice) in Image tab. I think that's pretty misleading -- it's better if it errors and gives a error message to the user that it couldn't connect.

Rylern commented 7 months ago

When opening an image with the ICE API, two (or three) URLs will be tested:

If connecting to one address doesn't work, the next one is tested.

I think the log you mentionned refers to the connection attempt on the omero.server address indicated on /api/v0/servers/. It didn't work, but the address indicated by you in the Settings window did, so the image opened. You won't see OMERO (Ice) in the Image tab if the connection failed or if another API is used.

The log message was not very clear about this behavior. I improved it in the v0.1.0-rc3 release.

psobolewskiPhD commented 7 months ago

I no longer see any logs from the extension when logging in or doing things. I'm still not sure I understand how everything works -- how are my credentials handled? When I log in via the extension to the web server, we only see a web login on our servers, no login on the actual omero server. I don't get a prompt for the omero server -- are you caching my credentials? Also when opening an image via Ice (in theory) the URI points to a location on the web server.

Edit: So I've tested it a few more time and I'm convinced the Ice access is working (pixel values are different than web), but I still find the wrong URI a source of confusion if trying to reproduce something. Plus something in the log that a connection was made or something. And if credentials are being cached or something, I think I'd rather just get prompted again to login when switching to Ice mode. The default mode can be web -- its much faster -- and Ice would be the opt-in.

Rylern commented 7 months ago

I no longer see any logs from the extension when logging in or doing things.

I only used logs to display warnings or errors, so it means everything works fine.

I'm still not sure I understand how everything works -- how are my credentials handled? When I log in via the extension to the web server, we only see a web login on our servers, no login on the actual omero server. I don't get a prompt for the omero server -- are you caching my credentials?

When you log in via the extension to the browser, a connection is made to the omero.web instance.

When you open an image with the ICE API, a new connection is made to the omero.server instance. The connection is made using a session UUID got from the web API (see the response of this request) which can be used instead of a username/password. So only this session UUID is cached, not the password.

Also when opening an image via Ice (in theory) the URI points to a location on the web server.

The pixel APIs (Web API, Ice API, pixel data microservice API) are only about retrieving pixel values. Everything else (for example image metadata) is retrieved using the omero.web instance. It's implemented this way to minimize the code specific to each pixel API, and because only pixel values will differ between the different APIs (the image width will be the same if retrieved through the omero.server instance or the omero.web instance for example).

This is why the URI points to a location on the web server. In practise, if the ICE API is selected, the URI by itself won't be used, but the image ID (contained in the URI) will.

In other words, the same OMERO image can be accessed from different URIs (e.g. /webgateway/img_detail/5628/, /webclient/?show=image-5628, /figure/new/?image=5628). What uniquely identifies an image is its ID (5628 here) and the omero.web address. So the URI is not important, only the image ID and the omero.web address are. Also, (I think that) the concept of URI is not defined for an image hosted on an omero.server (there is no http link to access it, only Java functions that need the image ID and the server address).

psobolewskiPhD commented 7 months ago

Thanks for the detailed response, everything makes sense now. I'd suggest adding that to the README/documentation.

I do think that basic informative logging for the connection process would be useful?

On my end everything seems to be working, but I will let @lacan chime in.

Rylern commented 7 months ago

OK thanks, I updated the README and I added INFO logs when connecting / disconnecting. They will be shown on the next release.

Rylern commented 7 months ago

Hi @lacan, are you able to use ICE with the last version of the extension?

lacan commented 6 months ago

Hi, Sorry for the delay. Indeed I got it to work with the ICE interface. However importing the images is terribly slow for now.

After I click on "Import XXX to QuPath", it takes 15-30s to go from Checking for compatible image readers, then to "Preparing to add 1 image to project"

I will still be testing the Microservice interface when out IT manages to install it.

Rylern commented 5 months ago

Thanks, I improved the performance of the Ice pixel API in the v0.1.0-rc5 release.

Some images might still take some time to open when their lowest resolution is big, but I cannot really do something about it. At least now the image opening duration should be the same as for the BIOP extension, and images should be faster to read because several readers are now used at the same time.

psobolewskiPhD commented 5 months ago

Testing now a 90kx40k ndpi, remote, over VPN, using the biop omero-raw takes about a minute from when I double click the image to getting the choose type dialog with rc5 using this plugin, the same image over the same connection takes 10-20s. Zooming in on a spot feels much faster.