1
2 """
3
4 Copyright 2009 Glencoe Software, Inc. All rights reserved.
5 Use is subject to license terms supplied in LICENSE.txt
6
7 """
8
9 __save__ = __name__
10 __name__ = 'omero'
11 try:
12 api = __import__('omero.api')
13 model = __import__('omero.model')
14 util = __import__('omero.util')
15 sys = __import__('omero.sys')
16 import omero.all
17 finally:
18 __name__ = __save__
19 del __save__
20
21 sys = __import__("sys")
22 import exceptions, traceback, threading, logging
23 import Ice, Glacier2, Glacier2_Router_ice
24 import omero_ext.uuid as uuid
25
27 """
28 Central client-side blitz entry point, and should be in sync with OmeroJava's omero.client
29 and OmeroCpp's omero::client.
30
31 Typical usage includes::
32
33 client = omero.client() # Uses --Ice.Config argument or ICE_CONFIG variable
34 client = omero.client(host = host) # Defines "omero.host"
35 client = omero.client(host = host, port = port) # Defines "omero.host" and "omero.port"
36
37 For more information, see:
38
39 - U{https://trac.openmicroscopy.org.uk/omero/wiki/ClientDesign}
40
41 """
42
43 - def __init__(self, args = None, id = None, \
44 host = None, port = None, pmap = None):
45 """
46 Constructor which takes one sys.argv-style list, one initialization
47 data, one host string, one port integer, and one properties map, in
48 that order. *However*, to simplify use, we reassign values based on
49 their type with a warning printed. A cleaner approach is to use named
50 parameters.
51 ::
52 c1 = omero.client(None, None, "host", myPort) # Correct
53 c2 = omero.client(host = "host", port = myPort) # Correct
54 c3 = omero.client("host", myPort) # Works with warning
55
56 Both "Ice" and "omero" prefixed properties will be parsed.
57
58 Defines the state variables::
59 __previous : InitializationData from any previous communicator, if any
60 Used to re-initialization the client post-closeSession()
61
62 __ic : communicator. Nullness => init() needed on createSession()
63
64 __sf : current session. Nullness => createSession() needed.
65
66 __resources: if non-null, hs access to this client instance and will
67 periodically call sf.keepAlive(None) in order to keep any
68 session alive. This can be enabled either via the omero.keep_alive
69 configuration property, or by calling the enableKeepAlive() method.
70 Once enabled, the period cannot be adjusted during a single
71 session.
72
73 Modifying these variables outside of the accessors can lead to
74 undefined behavior.
75
76 Equivalent to all OmeroJava and OmeroCpp constructors
77 """
78
79
80 self.__agent = "OMERO.py"
81 self.__insecure = False
82 self.__previous = None
83 self.__ic = None
84 self.__oa = None
85 self.__sf = None
86 self.__uuid = None
87 self.__resources = None
88 self.__lock = threading.RLock()
89
90
91 self.__logger = logging.getLogger("omero.client")
92 logging.basicConfig()
93
94
95
96 args, id, host, port, pmap = self._repair(args, id, host, port, pmap)
97
98
99 if not args:
100 args = []
101 else:
102
103
104
105 for idx, arg in enumerate(args):
106 if isinstance(arg, unicode):
107 arg = arg.encode("utf-8")
108 args[idx] = arg
109
110
111 if id == None:
112 id = Ice.InitializationData()
113
114 if id.properties == None:
115 id.properties = Ice.createProperties(args)
116
117 id.properties.parseCommandLineOptions("omero", args);
118 if host:
119 id.properties.setProperty("omero.host", str(host))
120 if not port:
121 port = id.properties.getPropertyWithDefault("omero.port",\
122 str(omero.constants.GLACIER2PORT))
123 id.properties.setProperty("omero.port", str(port))
124 if pmap:
125 for k,v in pmap.items():
126 id.properties.setProperty(str(k), str(v))
127
128 self._initData(id)
129
130 - def _repair(self, args, id, host, port, pmap):
131 """
132 Takes the 5 arguments passed to the __init__ method
133 and attempts to re-order them based on their types.
134 This allows for simplified usage without parameter
135 names.
136 """
137 types = [list, Ice.InitializationData, str, int, dict]
138 original = [args, id, host, port, pmap]
139 repaired = [None, None, None, None, None]
140
141
142 valid = True
143 for i in range(0, len(types)):
144 if None != original[i] and not isinstance(original[i], types[i]):
145 valid = False
146 break
147 if valid:
148 return original
149
150
151 for i in range(0, len(types)):
152 found = None
153 for j in range(0, len(types)):
154 if isinstance(original[j], types[i]):
155 if not found:
156 found = original[j]
157 else:
158 raise omero.ClientError("Found two arguments of same type: " + str(types[i]))
159 if found:
160 repaired[i] = found
161 return repaired
162
164 """
165 Initializes the current client via an Ice.InitializationData
166 instance. This is called by all of the constructors, but may
167 also be called on createSession(name, pass) if a previous
168 call to closeSession() has nulled the Ice.Communicator.
169 """
170
171 if not id:
172 raise omero.ClientError("No initialization data provided.");
173
174
175 id.properties.setProperty("Ice.ImplicitContext", "Shared")
176 id.properties.setProperty("Ice.ACM.Client", "0")
177 id.properties.setProperty("Ice.RetryIntervals", "-1")
178 id.properties.setProperty("Ice.Default.EndpointSelection", "Ordered")
179 id.properties.setProperty("Ice.Default.PreferSecure", "1")
180 id.properties.setProperty("Ice.Plugin.IceSSL" , "IceSSL:createIceSSL")
181 id.properties.setProperty("IceSSL.Ciphers" , "ADH")
182 id.properties.setProperty("IceSSL.VerifyPeer" , "0")
183
184
185 messageSize = id.properties.getProperty("Ice.MessageSizeMax")
186 if not messageSize or len(messageSize) == 0:
187 id.properties.setProperty("Ice.MessageSizeMax", str(omero.constants.MESSAGESIZEMAX))
188
189
190 self.parseAndSetInt(id, "Ice.Override.ConnectTimeout",\
191 omero.constants.CONNECTTIMEOUT)
192
193
194 endpoints = id.properties.getProperty("omero.ClientCallback.Endpoints")
195 if not endpoints or len(endpoints) == 0:
196 id.properties.setProperty("omero.ClientCallback.Endpoints", "tcp")
197
198
199 threadpool = id.properties.getProperty("omero.ClientCallback.ThreadPool.Size")
200 if not threadpool or len(threadpool) == 0:
201 id.properties.setProperty("omero.ClientCallback.ThreadPool.Size", str(omero.constants.CLIENTTHREADPOOLSIZE))
202
203
204 port = self.parseAndSetInt(id, "omero.port",\
205 omero.constants.GLACIER2PORT)
206
207
208 router = id.properties.getProperty("Ice.Default.Router")
209 if not router or len(router) == 0:
210 router = str(omero.constants.DEFAULTROUTER)
211 host = id.properties.getPropertyWithDefault("omero.host", """<"omero.host" not set>""")
212 router = router.replace("@omero.port@", str(port))
213 router = router.replace("@omero.host@", str(host))
214 id.properties.setProperty("Ice.Default.Router", router)
215
216
217 dump = id.properties.getProperty("omero.dump")
218 if len(dump) > 0:
219 m = self.getPropertyMap(id.properties)
220 keys = list(m.keys())
221 keys.sort()
222 for key in keys:
223 print "%s=%s" % (key, m[key])
224
225 self.__lock.acquire()
226 try:
227 if self.__ic:
228 raise omero.ClientError("Client already initialized")
229
230 try:
231 self.__ic = Ice.initialize(id)
232 except Ice.EndpointParseException:
233 msg = "No host specified. "
234 msg += "Use omero.client(HOSTNAME), ICE_CONFIG, or similar."
235 raise omero.ClientError(msg)
236
237 if not self.__ic:
238 raise omero.ClientError("Improper initialization")
239
240
241 self.of = ObjectFactory()
242 self.of.registerObjectFactory(self.__ic)
243 for of in omero.rtypes.ObjectFactories.values():
244 of.register(self.__ic)
245
246
247 self.__uuid = str(uuid.uuid4())
248 ctx = self.__ic.getImplicitContext()
249 if not ctx:
250 raise omero.ClientError("Ice.ImplicitContext not set to Shared")
251 ctx.put(omero.constants.CLIENTUUID, self.__uuid)
252
253
254 group = id.properties.getPropertyWithDefault("omero.group", "")
255 if group:
256 ctx.put("omero.group", group)
257
258
259 self.__oa = self.__ic.createObjectAdapter("omero.ClientCallback")
260 cb = BaseClient.CallbackI(self.__ic, self.__oa)
261 self.__oa.add(cb, self.__ic.stringToIdentity("ClientCallback/%s" % self.__uuid))
262 self.__oa.activate()
263 finally:
264 self.__lock.release()
265
267 """
268 Sets the omero.model.Session#getUserAgent() string for
269 this client. Every session creation will be passed this argument. Finding
270 open sesssions with the same agent can be done via
271 omero.api.ISessionPrx#getMyOpenAgentSessions(String).
272 """
273 self.__agent = agent
274
276 """
277 Specifies whether or not this client was created via a call to
278 createClient with a boolean of False. If insecure, then all
279 remote calls will use the insecure connection defined by the server.
280 """
281 return not self.__insecure
282
284 """
285 Creates a possibly insecure omero.client instance and calls joinSession
286 using the current getSessionId value. If secure is False, then first the
287 "omero.router.insecure" configuration property is retrieved from the server
288 and used as the value of "Ice.Default.Router" for the new client. Any exception
289 thrown during creation is passed on to the caller.
290 """
291 props = self.getPropertyMap()
292 if not secure:
293 insecure = self.getSession().getConfigService().getConfigValue("omero.router.insecure")
294 if insecure is not None and insecure != "":
295 props["Ice.Default.Router"] = insecure
296 else:
297 self.__logger.warn("Could not retrieve \"omero.router.insecure\"")
298
299 nClient = omero.client(props)
300 nClient.__insecure = not secure
301 nClient.setAgent("%s;secure=%s" % (self.__agent, secure))
302 nClient.joinSession(self.getSessionId())
303 return nClient
304
306 """
307 Calls closeSession() and ignores any exceptions.
308
309 Equivalent to close() in OmeroJava or omero::client::~client()
310 """
311 try:
312 self.closeSession()
313 except exceptions.Exception, e:
314 self.__logger.warning("Ignoring error in client.__del__:" + str(e.__class__))
315
317 """
318 Returns the Ice.Communicator for this instance or throws
319 an exception if None.
320 """
321 self.__lock.acquire()
322 try:
323 if not self.__ic:
324 raise omero.ClientError("No Ice.Communicator active; call createSession() or create a new client instance")
325 return self.__ic
326 finally:
327 self.__lock.release()
328
330 """
331 Returns the Ice.ObjectAdapter for this instance or throws
332 an exception if None.
333 """
334 self.__lock.acquire()
335 try:
336 if not self.__oa:
337 raise omero.ClientError("No Ice.ObjectAdapter active; call createSession() or create a new client instance")
338 return self.__oa
339 finally:
340 self.__lock.release()
341
343 """
344 Returns the current active session or throws an exception if none has been
345 created since the last closeSession()
346 """
347 self.__lock.acquire()
348 try:
349 sf = self.__sf
350 if not sf:
351 raise omero.ClientError("No session available")
352 return sf
353 finally:
354 self.__lock.release()
355
357 """
358 Returns the UUID for the current session without making a remote call.
359 Uses getSession() internally and will throw an exception if no session
360 is active.
361 """
362 return self.getSession().ice_getIdentity().name
363
365 """
366 Returns the Ice.ImplicitContext which defines what properties
367 will be sent on every method invocation.
368 """
369 return self.getCommunicator().getImplicitContext()
370
372 """
373 Returns the active properties for this instance
374 """
375 self.__lock.acquire()
376 try:
377 return self.__ic.getProperties()
378 finally:
379 self.__lock.release()
380
386
388 """
389 Returns all properties which are prefixed with "omero." or "Ice."
390 """
391 if properties is None:
392 properties = self.getProperties()
393
394 rv = dict()
395 for prefix in ["omero","Ice"]:
396 for k,v in properties.getPropertiesForPrefix(prefix).items():
397 rv[k] = v
398 return rv
399
401 """
402 Uses the given session uuid as name
403 and password to rejoin a running session
404 """
405 return self.createSession(session, session)
406
408 """
409 Performs the actual logic of logging in, which is done via the
410 getRouter(). Disallows an extant ServiceFactoryPrx, and
411 tries to re-create a null Ice.Communicator. A null or empty
412 username will throw an exception, but an empty password is allowed.
413 """
414 import omero
415
416 self.__lock.acquire()
417 try:
418
419
420
421 if self.__sf:
422 raise omero.ClientError("Session already active. Create a new omero.client or closeSession()")
423
424 if not self.__ic:
425 if not self.__previous:
426 raise omero.ClientError("No previous data to recreate communicator.")
427 self._initData(self.__previous)
428 self.__previous = None
429
430
431
432 if not username:
433 username = self.getProperty("omero.user")
434 elif isinstance(username,omero.RString):
435 username = username.val
436
437 if not username or len(username) == 0:
438 raise omero.ClientError("No username specified")
439
440 if not password:
441 password = self.getProperty("omero.pass")
442 elif isinstance(password,omero.RString):
443 password = password.val
444
445 if not password:
446 raise omero.ClientError("No password specified")
447
448
449 prx = None
450 retries = 0
451 while retries < 3:
452 reason = None
453 if retries > 0:
454 self.__logger.warning(\
455 "%s - createSession retry: %s"% (reason, retries) )
456 try:
457 ctx = dict(self.getImplicitContext().getContext())
458 ctx[omero.constants.AGENT] = self.__agent
459 prx = self.getRouter(self.__ic).createSession(username, password, ctx)
460 break
461 except omero.WrappedCreateSessionException, wrapped:
462 if not wrapped.concurrency:
463 raise wrapped
464 reason = "%s:%s" % (wrapped.type, wrapped.reason)
465 retries = retries + 1
466 except Ice.ConnectTimeoutException, cte:
467 reason = "Ice.ConnectTimeoutException:%s" % str(cte)
468 retries = retries + 1
469
470 if not prx:
471 raise omero.ClientError("Obtained null object prox")
472
473
474 self.__sf = omero.api.ServiceFactoryPrx.uncheckedCast(prx)
475 if not self.__sf:
476 raise omero.ClientError("Obtained object proxy is not a ServiceFactory")
477
478
479 keep_alive = self.__ic.getProperties().getPropertyWithDefault("omero.keep_alive", "-1")
480 try:
481 i = int(keep_alive)
482 self.enableKeepAlive(i)
483 except:
484 pass
485
486
487
488 try:
489 id = self.__ic.stringToIdentity("ClientCallback/%s" % self.__uuid )
490 raw = self.__oa.createProxy(id)
491 self.__sf.setCallback(omero.api.ClientCallbackPrx.uncheckedCast(raw))
492
493 except:
494 self.__del__()
495 raise
496
497
498 self.getImplicitContext().put(omero.constants.SESSIONUUID, self.getSessionId())
499
500 return self.__sf
501 finally:
502 self.__lock.release()
503
505 """
506 Resets the "omero.keep_alive" property on the current
507 Ice.Communicator which is used on initialization to determine
508 the time-period between Resource checks. If no __resources
509 instance is available currently, one is also created.
510 """
511
512 self.__lock.acquire()
513 try:
514
515 ic = self.getCommunicator()
516
517
518
519 ic.getProperties().setProperty("omero.keep_alive", str(seconds))
520
521
522
523 if self.__resources == None and seconds > 0:
524 self.__resources = omero.util.Resources(seconds)
525 class Entry:
526 def __init__(self, c):
527 self.c = c
528 def cleanup(self): pass
529 def check(self):
530 sf = self.c._BaseClient__sf
531 ic = self.c._BaseClient__ic
532 if sf != None:
533 try:
534 sf.keepAlive(None)
535 except exceptions.Exception, e:
536 if ic != None:
537 ic.getLogger().warning("Proxy keep alive failed.")
538 return False
539 return True
540 self.__resources.add(Entry(self))
541 finally:
542 self.__lock.release()
543
545 """
546 Acquires the default router, and throws an exception
547 if it is not of type Glacier2.Router. Also sets the
548 Ice.ImplicitContext on the router proxy.
549 """
550 prx = comm.getDefaultRouter()
551 if not prx:
552 raise omero.ClientError("No default router found.")
553 router = Glacier2.RouterPrx.uncheckedCast(prx)
554 if not router:
555 raise omero.ClientError("Error obtaining Glacier2 router")
556
557
558
559 router = router.ice_context(comm.getImplicitContext().getContext())
560 return router
561
562 - def sha1(self, filename):
563 """
564 Calculates the local sha1 for a file.
565 """
566 try:
567 from hashlib import sha1 as sha_new
568 except ImportError:
569 from sha import new as sha_new
570 digest = sha_new()
571 file = open(filename, 'rb')
572 try:
573 while True:
574 block = file.read(1024)
575 if not block:
576 break
577 digest.update(block)
578 finally:
579 file.close()
580 return digest.hexdigest()
581
582 - def upload(self, filename, name = None, path = None,
583 type = None, ofile = None, block_size = 1024):
651
652 - def download(self, ofile, filename = None, block_size = 1024*1024, filehandle = None):
653 prx = self.__sf.createRawFileStore()
654 try:
655 if not ofile or not ofile.id:
656 raise omero.ClientError("No file to download")
657 ofile = self.__sf.getQueryService().get("OriginalFile", ofile.id.val)
658
659 if block_size > ofile.size.val:
660 block_size = ofile.size.val
661
662 prx.setFileId(ofile.id.val)
663
664 size = ofile.size.val
665 offset = 0
666
667 if filehandle is None:
668 if filename is None:
669 raise omero.ClientError("no filename or filehandle specified")
670 filehandle = open(filename, 'wb')
671 else:
672 if filename:
673 raise omero.ClientError("filename and filehandle specified.")
674
675 try:
676 while (offset+block_size) < size:
677 filehandle.write(prx.read(offset, block_size))
678 offset += block_size
679 filehandle.write(prx.read(offset, (size-offset)))
680 finally:
681 if filename:
682 filehandle.close()
683 finally:
684 prx.close()
685
687 """
688 Returns all active StatefulServiceInterface proxies. This can
689 be used to call close before calling setSecurityContext.
690 """
691 rv = []
692 sf = self.sf
693 services = sf.activeServices()
694 for srv in services:
695 try:
696 prx = sf.getByName(srv)
697 prx = omero.api.StatefulServiceInterfacePrx.checkedCast(prx)
698 if prx is not None:
699 rv.append(prx)
700 except:
701 self.__logger.warn("Error looking up proxy: %s" % srv, exc_info=1)
702 return rv
703
705 """
706 Closes the Router connection created by createSession(). Due to a bug in Ice,
707 only one connection is allowed per communicator, so we also destroy the communicator.
708 """
709
710 self.__lock.acquire()
711 try:
712 self.__sf = None
713
714 oldOa = self.__oa
715 self.__oa = None
716
717 oldIc = self.__ic
718 self.__ic = None
719
720
721 if not oldIc:
722 return
723
724 if oldOa:
725 try:
726 oldOa.deactivate()
727 except exceptions.Exception, e:
728 self.__logger.warning("While deactivating adapter: " + str(e.message))
729
730 self.__previous = Ice.InitializationData()
731 self.__previous.properties = oldIc.getProperties().clone()
732
733 oldR = self.__resources
734 self.__resources = None
735 if oldR != None:
736 try:
737 oldR.cleanup()
738 except exceptions.Exception, e:
739 oldIc.getLogger().warning(
740 "While cleaning up resources: " + str(e))
741
742 try:
743 try:
744 self.getRouter(oldIc).destroySession()
745 except Glacier2.SessionNotExistException:
746
747 pass
748 except Ice.ConnectionLostException:
749
750 pass
751 except Ice.ConnectionRefusedException:
752
753 pass
754 except Ice.ConnectTimeoutException:
755
756 pass
757
758
759 finally:
760 oldIc.destroy()
761 del oldIc._impl
762
763 finally:
764 self.__lock.release()
765
766
768 """
769 Calls ISession.closeSession(omero.model.Session) until
770 the returned reference count is greater than zero. The
771 number of invocations is returned. If ISession.closeSession()
772 cannot be called, -1 is returned.
773 """
774
775 s = omero.model.SessionI()
776 s.uuid = omero.rtypes.rstring(self.getSessionId())
777 try:
778 svc = self.sf.getSessionService()
779 except:
780 self.__logger.warning("Cannot get session service for killSession. Using closeSession")
781 self.closeSession()
782 return -1;
783
784 count = 0
785 try:
786 r = 1
787 while r > 0:
788 count += 1
789 r = svc.closeSession(s)
790 except omero.RemovedSessionException:
791 pass
792 except:
793 self.__logger.warning("Unknown exception while closing all references", exc_info = True)
794
795
796 self.closeSession()
797 return count
798
799
800
801
802 - def _env(self, _unwrap, method, *args):
817
823
825 """
826 Retrieves an item from the "output" shared (session) memory.
827 """
828 return self._env(unwrap, "getOutput", key)
829
830
836
838 """
839 Sets an item in the "output" shared (session) memory under the given name.
840 """
841 self._env(False, "setOutput", key, value)
842
848
850 """
851 Returns a list of keys for all items in the "output" shared (session) memory
852 """
853 return self._env(False, "getOutputKeys")
854
860
862 """
863 Returns all items in the "output" shared (session) memory
864 """
865 return self._env(unwrap, "getOutputKeys")
866
867
868
869
877
879 """
880 Compatibility layer, which allows calls to getCommunicator() and getSession()
881 to be called via self.ic and self.sf
882 """
883 if name == "ic":
884 return self.getCommunicator()
885 elif name == "sf":
886 return self.getSession()
887 elif name == "adapter":
888 return self.getAdapter()
889 else:
890 raise AttributeError("Unknown property: " + name)
891
892
893
894
896 if not self.__oa:
897 raise omero.ClientError("No session active; call createSession()")
898 obj = self.__oa.find(self.ic.stringToIdentity("ClientCallback/%s" % self.__uuid))
899 if not isinstance(obj, BaseClient.CallbackI):
900 raise omero.ClientError("Cannot find CallbackI in ObjectAdapter")
901 return obj
902
905
908
911
913 """
914 Implemention of ClientCallback which will be added to
915 any Session which this instance creates. Note: this client
916 should avoid all interaction with the {@link client#lock} since it
917 can lead to deadlocks during shutdown. See: ticket:1210
918 """
919
920
921
922
926 try:
927 self.oa.deactivate();
928 except exceptions.Exception, e:
929 sys.err.write("On session closed: " + str(e))
930
937 - def execute(self, myCallable, action):
938 try:
939 myCallable()
940
941 except:
942 try:
943 self.ic.getLogger().error("Error performing %s" % action)
944 except:
945 print "Error performing %s" % action
946
949 - def shutdownIn(self, milliseconds, current = None):
953
954
955
956
957
958 import util.FactoryMap
960 """
961 Responsible for instantiating objects during deserialization.
962 """
963
966
968 for key in self.__m:
969 if not ic.findObjectFactory(key):
970 ic.addObjectFactory(self,key)
971
973 generator = self.__m[type]
974 if generator == None:
975 raise omero.ClientError("Unknown type:"+type)
976 return generator.next()
977
981