1
2
3 """
4
5 Copyright 2009 Glencoe Software, Inc. All rights reserved.
6 Use is subject to license terms supplied in LICENSE.txt
7
8 """
9
10 __save__ = __name__
11 __name__ = 'omero'
12 try:
13 api = __import__('omero.api')
14 model = __import__('omero.model')
15 util = __import__('omero.util')
16 sys = __import__('omero.sys')
17 import omero.all
18 finally:
19 __name__ = __save__
20 del __save__
21
22 sys = __import__("sys")
23 import traceback, threading, logging
24 import IceImport, Ice
25 import omero_ext.uuid as uuid
26
27 IceImport.load("Glacier2_Router_ice")
28 import Glacier2
29
31 """
32 Central client-side blitz entry point, and should be in sync with OmeroJava's omero.client
33 and OmeroCpp's omero::client.
34
35 Typical usage includes::
36
37 client = omero.client() # Uses --Ice.Config argument or ICE_CONFIG variable
38 client = omero.client(host = host) # Defines "omero.host"
39 client = omero.client(host = host, port = port) # Defines "omero.host" and "omero.port"
40
41 For more information, see:
42
43 - U{http://trac.openmicroscopy.org.uk/ome/wiki/ClientDesign}
44
45 """
46
47 - def __init__(self, args = None, id = None, \
48 host = None, port = None, pmap = None):
49 """
50 Constructor which takes one sys.argv-style list, one initialization
51 data, one host string, one port integer, and one properties map, in
52 that order. *However*, to simplify use, we reassign values based on
53 their type with a warning printed. A cleaner approach is to use named
54 parameters.
55 ::
56 c1 = omero.client(None, None, "host", myPort) # Correct
57 c2 = omero.client(host = "host", port = myPort) # Correct
58 c3 = omero.client("host", myPort) # Works with warning
59
60 Both "Ice" and "omero" prefixed properties will be parsed.
61
62 Defines the state variables::
63 __previous : InitializationData from any previous communicator, if any
64 Used to re-initialization the client post-closeSession()
65
66 __ic : communicator. Nullness => init() needed on createSession()
67
68 __sf : current session. Nullness => createSession() needed.
69
70 __resources: if non-null, hs access to this client instance and will
71 periodically call sf.keepAlive(None) in order to keep any
72 session alive. This can be enabled either via the omero.keep_alive
73 configuration property, or by calling the enableKeepAlive() method.
74 Once enabled, the period cannot be adjusted during a single
75 session.
76
77 Modifying these variables outside of the accessors can lead to
78 undefined behavior.
79
80 Equivalent to all OmeroJava and OmeroCpp constructors
81 """
82
83
84 self.__agent = "OMERO.py"
85 self.__insecure = False
86 self.__previous = None
87 self.__ic = None
88 self.__oa = None
89 self.__cb = None
90 self.__sf = None
91 self.__uuid = None
92 self.__resources = None
93 self.__lock = threading.RLock()
94
95
96 self.__logger = logging.getLogger("omero.client")
97 logging.basicConfig()
98
99
100
101 args, id, host, port, pmap = self._repair(args, id, host, port, pmap)
102
103
104 if not args:
105 args = []
106 else:
107
108
109
110 for idx, arg in enumerate(args):
111 if isinstance(arg, unicode):
112 arg = arg.encode("utf-8")
113 args[idx] = arg
114
115
116 if id == None:
117 id = Ice.InitializationData()
118
119 if id.properties == None:
120 id.properties = Ice.createProperties(args)
121
122 id.properties.parseCommandLineOptions("omero", args);
123 if host:
124 id.properties.setProperty("omero.host", str(host))
125 if not port:
126 port = id.properties.getPropertyWithDefault("omero.port",\
127 str(omero.constants.GLACIER2PORT))
128 id.properties.setProperty("omero.port", str(port))
129 if pmap:
130 for k,v in pmap.items():
131 id.properties.setProperty(str(k), str(v))
132
133 self._initData(id)
134
135 - def _repair(self, args, id, host, port, pmap):
136 """
137 Takes the 5 arguments passed to the __init__ method
138 and attempts to re-order them based on their types.
139 This allows for simplified usage without parameter
140 names.
141 """
142 types = [list, Ice.InitializationData, str, int, dict]
143 original = [args, id, host, port, pmap]
144 repaired = [None, None, None, None, None]
145
146
147 valid = True
148 for i in range(0, len(types)):
149 if None != original[i] and not isinstance(original[i], types[i]):
150 valid = False
151 break
152 if valid:
153 return original
154
155
156 for i in range(0, len(types)):
157 found = None
158 for j in range(0, len(types)):
159 if isinstance(original[j], types[i]):
160 if not found:
161 found = original[j]
162 else:
163 raise omero.ClientError("Found two arguments of same type: " + str(types[i]))
164 if found:
165 repaired[i] = found
166 return repaired
167
169 """
170 Initializes the current client via an Ice.InitializationData
171 instance. This is called by all of the constructors, but may
172 also be called on createSession(name, pass) if a previous
173 call to closeSession() has nulled the Ice.Communicator.
174 """
175
176 if not id:
177 raise omero.ClientError("No initialization data provided.");
178
179
180 id.properties.setProperty("Ice.ImplicitContext", "Shared")
181 id.properties.setProperty("Ice.ACM.Client", "0")
182 id.properties.setProperty("Ice.CacheMessageBuffers", "0")
183 id.properties.setProperty("Ice.RetryIntervals", "-1")
184 id.properties.setProperty("Ice.Default.EndpointSelection", "Ordered")
185 id.properties.setProperty("Ice.Default.PreferSecure", "1")
186 id.properties.setProperty("Ice.Plugin.IceSSL" , "IceSSL:createIceSSL")
187 id.properties.setProperty("IceSSL.Ciphers" , "ADH")
188 id.properties.setProperty("IceSSL.VerifyPeer" , "0")
189
190
191
192 if Ice.intVersion() >= 30500:
193 if not id.properties.getProperty("Ice.Default.EncodingVersion"):
194 id.properties.setProperty("Ice.Default.EncodingVersion", "1.0")
195
196
197 messageSize = id.properties.getProperty("Ice.MessageSizeMax")
198 if not messageSize or len(messageSize) == 0:
199 id.properties.setProperty("Ice.MessageSizeMax", str(omero.constants.MESSAGESIZEMAX))
200
201
202 self.parseAndSetInt(id, "Ice.Override.ConnectTimeout",\
203 omero.constants.CONNECTTIMEOUT)
204
205
206 for x in ("Client", "Server"):
207 sizemax = id.properties.getProperty("Ice.ThreadPool.%s.SizeMax" % x)
208 if not sizemax or len(sizemax) == 0:
209 id.properties.setProperty("Ice.ThreadPool.%s.SizeMax" % x, "50")
210
211
212 port = self.parseAndSetInt(id, "omero.port",\
213 omero.constants.GLACIER2PORT)
214
215
216 router = id.properties.getProperty("Ice.Default.Router")
217 if not router or len(router) == 0:
218 router = str(omero.constants.DEFAULTROUTER)
219 host = id.properties.getPropertyWithDefault("omero.host", """<"omero.host" not set>""")
220 router = router.replace("@omero.port@", str(port))
221 router = router.replace("@omero.host@", str(host))
222 id.properties.setProperty("Ice.Default.Router", router)
223
224
225 dump = id.properties.getProperty("omero.dump")
226 if len(dump) > 0:
227 m = self.getPropertyMap(id.properties)
228 keys = list(m.keys())
229 keys.sort()
230 for key in keys:
231 print "%s=%s" % (key, m[key])
232
233 self.__lock.acquire()
234 try:
235 if self.__ic:
236 raise omero.ClientError("Client already initialized")
237
238 try:
239 self.__ic = Ice.initialize(id)
240 except Ice.EndpointParseException:
241 msg = "No host specified. "
242 msg += "Use omero.client(HOSTNAME), ICE_CONFIG, or similar."
243 raise omero.ClientError(msg)
244
245 if not self.__ic:
246 raise omero.ClientError("Improper initialization")
247
248
249 import ObjectFactoryRegistrar as ofr
250 ofr.registerObjectFactory(self.__ic, self)
251
252 for of in omero.rtypes.ObjectFactories.values():
253 of.register(self.__ic)
254
255
256 self.__uuid = str(uuid.uuid4())
257 ctx = self.__ic.getImplicitContext()
258 if not ctx:
259 raise omero.ClientError("Ice.ImplicitContext not set to Shared")
260 ctx.put(omero.constants.CLIENTUUID, self.__uuid)
261
262
263 group = id.properties.getPropertyWithDefault("omero.group", "")
264 if group:
265 ctx.put("omero.group", group)
266
267 finally:
268 self.__lock.release()
269
271 """
272 Sets the omero.model.Session#getUserAgent() string for
273 this client. Every session creation will be passed this argument. Finding
274 open sesssions with the same agent can be done via
275 omero.api.ISessionPrx#getMyOpenAgentSessions(String).
276 """
277 self.__agent = agent
278
280 """
281 Specifies whether or not this client was created via a call to
282 createClient with a boolean of False. If insecure, then all
283 remote calls will use the insecure connection defined by the server.
284 """
285 return not self.__insecure
286
288 """
289 Creates a possibly insecure omero.client instance and calls joinSession
290 using the current getSessionId value. If secure is False, then first the
291 "omero.router.insecure" configuration property is retrieved from the server
292 and used as the value of "Ice.Default.Router" for the new client. Any exception
293 thrown during creation is passed on to the caller.
294
295 Note: detachOnDestroy has NOT been called on the session in the returned client.
296 Clients are responsible for doing this immediately if such desired.
297 """
298 props = self.getPropertyMap()
299 if not secure:
300 insecure = self.getSession().getConfigService().getConfigValue("omero.router.insecure")
301 if insecure is not None and insecure != "":
302 props["Ice.Default.Router"] = insecure
303 else:
304 self.__logger.warn("Could not retrieve \"omero.router.insecure\"")
305
306 nClient = omero.client(props)
307 nClient.__insecure = not secure
308 nClient.setAgent("%s;secure=%s" % (self.__agent, secure))
309 nClient.joinSession(self.getSessionId())
310 return nClient
311
313 """
314 Calls closeSession() and ignores any exceptions.
315
316 Equivalent to close() in OmeroJava or omero::client::~client()
317 """
318 try:
319 self.closeSession()
320 except Exception, e:
321
322
323
324 if 'Glacier2.SessionNotExistException' not in str(e.__class__):
325 self.__logger.warning("..Ignoring error in client.__del__:" + str(e.__class__))
326
328 """
329 Returns the Ice.Communicator for this instance or throws
330 an exception if None.
331 """
332 self.__lock.acquire()
333 try:
334 if not self.__ic:
335 raise omero.ClientError("No Ice.Communicator active; call createSession() or create a new client instance")
336 return self.__ic
337 finally:
338 self.__lock.release()
339
341 """
342 Returns the Ice.ObjectAdapter for this instance or throws
343 an exception if None.
344 """
345 self.__lock.acquire()
346 try:
347 if not self.__oa:
348 raise omero.ClientError("No Ice.ObjectAdapter active; call createSession() or create a new client instance")
349 return self.__oa
350 finally:
351 self.__lock.release()
352
354 """
355 Returns the current active session or throws an exception if none has been
356 created since the last closeSession()
357
358 If blocking is False, then self.__lock is not acquired and the value
359 of self.__sf is simply returned. Clients must properly handle the
360 situation where this value is None.
361 """
362 if not blocking:
363 return self.__sf
364
365 self.__lock.acquire(blocking)
366 try:
367 sf = self.__sf
368 if not sf:
369 raise omero.ClientError("No session available")
370 return sf
371 finally:
372 self.__lock.release()
373
375 """
376 Returns the UUID for the current session without making a remote call.
377 Uses getSession() internally and will throw an exception if no session
378 is active.
379 """
380 return self.getSession().ice_getIdentity().name
381
383 """
384 Returns the category which should be used for all callbacks
385 passed to the server.
386 """
387 return self.getRouter(self.__ic).getCategoryForClient()
388
390 """
391 Returns the Ice.ImplicitContext which defines what properties
392 will be sent on every method invocation.
393 """
394 return self.getCommunicator().getImplicitContext()
395
397 """
398 Returns the active properties for this instance
399 """
400 self.__lock.acquire()
401 try:
402 return self.__ic.getProperties()
403 finally:
404 self.__lock.release()
405
411
413 """
414 Returns all properties which are prefixed with "omero." or "Ice."
415 """
416 if properties is None:
417 properties = self.getProperties()
418
419 rv = dict()
420 for prefix in ["omero","Ice"]:
421 for k,v in properties.getPropertiesForPrefix(prefix).items():
422 rv[k] = v
423 return rv
424
426 """
427 Uses the given session uuid as name
428 and password to rejoin a running session
429 """
430 return self.createSession(session, session)
431
433 """
434 Performs the actual logic of logging in, which is done via the
435 getRouter(). Disallows an extant ServiceFactoryPrx, and
436 tries to re-create a null Ice.Communicator. A null or empty
437 username will throw an exception, but an empty password is allowed.
438 """
439 import omero
440
441 self.__lock.acquire()
442 try:
443
444
445
446 if self.__sf:
447 raise omero.ClientError("Session already active. Create a new omero.client or closeSession()")
448
449 if not self.__ic:
450 if not self.__previous:
451 raise omero.ClientError("No previous data to recreate communicator.")
452 self._initData(self.__previous)
453 self.__previous = None
454
455
456
457 if not username:
458 username = self.getProperty("omero.user")
459 elif isinstance(username,omero.RString):
460 username = username.val
461
462 if not username or len(username) == 0:
463 raise omero.ClientError("No username specified")
464
465 if not password:
466 password = self.getProperty("omero.pass")
467 elif isinstance(password,omero.RString):
468 password = password.val
469
470 if not password:
471 raise omero.ClientError("No password specified")
472
473
474 prx = None
475 retries = 0
476 while retries < 3:
477 reason = None
478 if retries > 0:
479 self.__logger.warning(\
480 "%s - createSession retry: %s"% (reason, retries) )
481 try:
482 ctx = dict(self.getImplicitContext().getContext())
483 ctx[omero.constants.AGENT] = self.__agent
484 rtr = self.getRouter(self.__ic)
485 prx = rtr.createSession(username, password, ctx)
486
487
488 self.__oa = self.__ic.createObjectAdapterWithRouter( \
489 "omero.ClientCallback", rtr)
490 self.__oa.activate()
491
492 id = Ice.Identity()
493 id.name = self.__uuid
494 id.category = rtr.getCategoryForClient()
495
496 self.__cb = BaseClient.CallbackI(self.__ic, self.__oa, id)
497 self.__oa.add(self.__cb, id)
498
499
500 break
501 except omero.WrappedCreateSessionException, wrapped:
502 if not wrapped.concurrency:
503 raise wrapped
504 reason = "%s:%s" % (wrapped.type, wrapped.reason)
505 retries = retries + 1
506 except Ice.ConnectTimeoutException, cte:
507 reason = "Ice.ConnectTimeoutException:%s" % str(cte)
508 retries = retries + 1
509
510 if not prx:
511 raise omero.ClientError("Obtained null object prox")
512
513
514 self.__sf = omero.api.ServiceFactoryPrx.uncheckedCast(prx)
515 if not self.__sf:
516 raise omero.ClientError("Obtained object proxy is not a ServiceFactory")
517
518
519 self.startKeepAlive()
520
521
522
523 try:
524
525 raw = self.__oa.createProxy(self.__cb.id)
526 self.__sf.setCallback(omero.api.ClientCallbackPrx.uncheckedCast(raw))
527
528 except:
529 self.__del__()
530 raise
531
532
533 self.getImplicitContext().put(omero.constants.SESSIONUUID, self.getSessionId())
534
535 return self.__sf
536 finally:
537 self.__lock.release()
538
540 """
541 Resets the "omero.keep_alive" property on the current
542 Ice.Communicator which is used on initialization to determine
543 the time-period between Resource checks. The __resources
544 instance will be created as soon as an active session is
545 detected.
546 """
547
548 self.__lock.acquire()
549 try:
550
551 ic = self.getCommunicator()
552
553
554
555 ic.getProperties().setProperty("omero.keep_alive", str(seconds))
556
557
558
559
560 if seconds <= 0:
561 self.stopKeepAlive()
562 else:
563 try:
564
565
566 self.getSession()
567 self.startKeepAlive()
568 except omero.ClientError:
569 pass
570 finally:
571 self.__lock.release()
572
574 """
575 Start a new __resources instance, stopping any that current exists
576 IF omero.keep_alive is greater than 1.
577 """
578 self.__lock.acquire()
579 try:
580 ic = self.getCommunicator()
581 props = ic.getProperties()
582 seconds = -1
583 try:
584 seconds = props.getPropertyWithDefault("omero.keep_alive", "-1")
585 seconds = int(seconds)
586 except ValueError:
587 pass
588
589
590 if self.__resources is not None:
591 self.stopKeepAlive()
592
593
594 if seconds > 0:
595 self.__resources = omero.util.Resources(seconds)
596 class Entry:
597 def __init__(self, c):
598 self.c = c
599 def cleanup(self): pass
600 def check(self):
601 sf = self.c._BaseClient__sf
602 ic = self.c._BaseClient__ic
603 if sf != None:
604 try:
605 sf.keepAlive(None)
606 except Exception, e:
607 if ic != None:
608 ic.getLogger().warning("Proxy keep alive failed.")
609 return False
610 return True
611 self.__resources.add(Entry(self))
612 finally:
613 self.__lock.release()
614
616 self.__lock.acquire()
617 try:
618 if self.__resources is not None:
619 try:
620 self.__resources.cleanup()
621 finally:
622 self.__resources = None
623
624 finally:
625 self.__lock.release()
626
628 """
629 Acquires the default router, and throws an exception
630 if it is not of type Glacier2.Router. Also sets the
631 Ice.ImplicitContext on the router proxy.
632 """
633 prx = comm.getDefaultRouter()
634 if not prx:
635 raise omero.ClientError("No default router found.")
636 router = Glacier2.RouterPrx.uncheckedCast(prx)
637 if not router:
638 raise omero.ClientError("Error obtaining Glacier2 router")
639
640
641
642 router = router.ice_context(comm.getImplicitContext().getContext())
643 return router
644
645 - def sha1(self, filename):
646 """
647 Calculates the local sha1 for a file.
648 """
649 try:
650 from hashlib import sha1 as sha_new
651 except ImportError:
652 from sha import new as sha_new
653 digest = sha_new()
654 file = open(filename, 'rb')
655 try:
656 while True:
657 block = file.read(1024)
658 if not block:
659 break
660 digest.update(block)
661 finally:
662 file.close()
663 return digest.hexdigest()
664
665 - def upload(self, filename, name = None, path = None,
666 type = None, ofile = None, block_size = 1024):
734
735 - def download(self, ofile, filename = None, block_size = 1024*1024, filehandle = None):
736 prx = self.__sf.createRawFileStore()
737 try:
738 if not ofile or not ofile.id:
739 raise omero.ClientError("No file to download")
740 ofile = self.__sf.getQueryService().get("OriginalFile", ofile.id.val)
741
742 if block_size > ofile.size.val:
743 block_size = ofile.size.val
744
745 prx.setFileId(ofile.id.val)
746
747 size = ofile.size.val
748 offset = 0
749
750 if filehandle is None:
751 if filename is None:
752 raise omero.ClientError("no filename or filehandle specified")
753 filehandle = open(filename, 'wb')
754 else:
755 if filename:
756 raise omero.ClientError("filename and filehandle specified.")
757
758 try:
759 while (offset+block_size) < size:
760 filehandle.write(prx.read(offset, block_size))
761 offset += block_size
762 filehandle.write(prx.read(offset, (size-offset)))
763 finally:
764 if filename:
765 filehandle.close()
766 finally:
767 prx.close()
768
770 """
771 Returns all active StatefulServiceInterface proxies. This can
772 be used to call close before calling setSecurityContext.
773 """
774 rv = []
775 sf = self.sf
776 services = sf.activeServices()
777 for srv in services:
778 try:
779 prx = sf.getByName(srv)
780 prx = omero.api.StatefulServiceInterfacePrx.checkedCast(prx)
781 if prx is not None:
782 rv.append(prx)
783 except:
784 self.__logger.warn("Error looking up proxy: %s" % srv, exc_info=1)
785 return rv
786
788 """
789 Closes the Router connection created by createSession(). Due to a bug in Ice,
790 only one connection is allowed per communicator, so we also destroy the communicator.
791 """
792
793 self.__lock.acquire()
794 try:
795
796 try:
797 self.stopKeepAlive()
798 except Exception, e:
799 oldIc.getLogger().warning(
800 "While cleaning up resources: " + str(e))
801
802 self.__sf = None
803
804 oldOa = self.__oa
805 self.__oa = None
806
807 oldIc = self.__ic
808 self.__ic = None
809
810
811 if not oldIc:
812 return
813
814 if oldOa:
815 try:
816 oldOa.deactivate()
817 except Exception, e:
818 self.__logger.warning("While deactivating adapter: " + str(e.message))
819
820 self.__previous = Ice.InitializationData()
821 self.__previous.properties = oldIc.getProperties().clone()
822
823 try:
824 try:
825 self.getRouter(oldIc).destroySession()
826 except Glacier2.SessionNotExistException:
827
828 pass
829 except Ice.ConnectionLostException:
830
831 pass
832 except Ice.ConnectionRefusedException:
833
834 pass
835 except Ice.ConnectTimeoutException:
836
837 pass
838
839
840 finally:
841 oldIc.destroy()
842 del oldIc._impl
843
844 finally:
845 self.__lock.release()
846
847
849 """
850 Calls ISession.closeSession(omero.model.Session) until
851 the returned reference count is greater than zero. The
852 number of invocations is returned. If ISession.closeSession()
853 cannot be called, -1 is returned.
854 """
855
856 s = omero.model.SessionI()
857 s.uuid = omero.rtypes.rstring(self.getSessionId())
858 try:
859 svc = self.sf.getSessionService()
860 except:
861 self.__logger.warning("Cannot get session service for killSession. Using closeSession")
862 self.closeSession()
863 return -1;
864
865 count = 0
866 try:
867 r = 1
868 while r > 0:
869 count += 1
870 r = svc.closeSession(s)
871 except omero.RemovedSessionException:
872 pass
873 except:
874 self.__logger.warning("Unknown exception while closing all references", exc_info = True)
875
876
877 self.closeSession()
878 return count
879
880
881
882
883 - def _env(self, _unwrap, method, *args):
898
904
906 """
907 Retrieves an item from the "output" shared (session) memory.
908 """
909 return self._env(unwrap, "getOutput", key)
910
911
917
919 """
920 Sets an item in the "output" shared (session) memory under the given name.
921 """
922 self._env(False, "setOutput", key, value)
923
929
931 """
932 Returns a list of keys for all items in the "output" shared (session) memory
933 """
934 return self._env(False, "getOutputKeys")
935
941
943 """
944 Returns all items in the "output" shared (session) memory
945 """
946 return self._env(unwrap, "getOutputKeys")
947
948
949
950
958
960 """
961 Compatibility layer, which allows calls to getCommunicator() and getSession()
962 to be called via self.ic and self.sf
963 """
964 if name == "ic":
965 return self.getCommunicator()
966 elif name == "sf":
967 return self.getSession()
968 elif name == "adapter":
969 return self.getAdapter()
970 else:
971 raise AttributeError("Unknown property: " + name)
972
973
974
975
978
981
984
986 """
987 Implemention of ClientCallback which will be added to
988 any Session which this instance creates. Note: this client
989 should avoid all interaction with the {@link client#lock} since it
990 can lead to deadlocks during shutdown. See: ticket:1210
991 """
992
993
994
995
999 try:
1000 self.oa.deactivate();
1001 except Exception, e:
1002 sys.err.write("On session closed: " + str(e))
1003
1011 - def execute(self, myCallable, action):
1012 try:
1013 myCallable()
1014
1015 except:
1016 try:
1017 self.ic.getLogger().error("Error performing %s" % action)
1018 except:
1019 print "Error performing %s" % action
1020
1023 - def shutdownIn(self, milliseconds, current = None):
1027