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 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 args = list(args)
103
104
105 if id == None:
106 id = Ice.InitializationData()
107
108 if id.properties == None:
109 id.properties = Ice.createProperties(args)
110
111 id.properties.parseCommandLineOptions("omero", args);
112 if host:
113 id.properties.setProperty("omero.host", str(host))
114 if not port:
115 port = id.properties.getPropertyWithDefault("omero.port",\
116 str(omero.constants.GLACIER2PORT))
117 id.properties.setProperty("omero.port", str(port))
118 if pmap:
119 for k,v in pmap.items():
120 id.properties.setProperty(str(k), str(v))
121
122 self._initData(id)
123
124 - def _repair(self, args, id, host, port, pmap):
125 """
126 Takes the 5 arguments passed to the __init__ method
127 and attempts to re-order them based on their types.
128 This allows for simplified usage without parameter
129 names.
130 """
131 types = [list, Ice.InitializationData, str, int, dict]
132 original = [args, id, host, port, pmap]
133 repaired = [None, None, None, None, None]
134
135
136 valid = True
137 for i in range(0, len(types)):
138 if None != original[i] and not isinstance(original[i], types[i]):
139 valid = False
140 break
141 if valid:
142 return original
143
144
145 for i in range(0, len(types)):
146 found = None
147 for j in range(0, len(types)):
148 if isinstance(original[j], types[i]):
149 if not found:
150 found = original[j]
151 else:
152 raise omero.ClientError("Found two arguments of same type: " + str(types[i]))
153 if found:
154 repaired[i] = found
155 return repaired
156
158 """
159 Initializes the current client via an Ice.InitializationData
160 instance. This is called by all of the constructors, but may
161 also be called on createSession(name, pass) if a previous
162 call to closeSession() has nulled the Ice.Communicator.
163 """
164
165 if not id:
166 raise ClientError("No initialization data provided.");
167
168
169 id.properties.setProperty("Ice.ImplicitContext", "Shared")
170 id.properties.setProperty("Ice.ACM.Client", "0")
171 id.properties.setProperty("Ice.RetryIntervals", "-1")
172 id.properties.setProperty("Ice.Default.EndpointSelection", "Ordered")
173 id.properties.setProperty("Ice.Default.PreferSecure", "1")
174 id.properties.setProperty("Ice.Plugin.IceSSL" , "IceSSL:createIceSSL")
175 id.properties.setProperty("IceSSL.Ciphers" , "ADH")
176 id.properties.setProperty("IceSSL.VerifyPeer" , "0")
177
178
179 messageSize = id.properties.getProperty("Ice.MessageSizeMax")
180 if not messageSize or len(messageSize) == 0:
181 id.properties.setProperty("Ice.MessageSizeMax", str(omero.constants.MESSAGESIZEMAX))
182
183
184 self.parseAndSetInt(id, "Ice.Override.ConnectTimeout",\
185 omero.constants.CONNECTTIMEOUT)
186
187
188 endpoints = id.properties.getProperty("omero.ClientCallback.Endpoints")
189 if not endpoints or len(endpoints) == 0:
190 id.properties.setProperty("omero.ClientCallback.Endpoints", "tcp")
191
192
193 threadpool = id.properties.getProperty("omero.ClientCallback.ThreadPool.Size")
194 if not threadpool or len(threadpool) == 0:
195 id.properties.setProperty("omero.ClientCallback.ThreadPool.Size", str(omero.constants.CLIENTTHREADPOOLSIZE))
196
197
198 port = self.parseAndSetInt(id, "omero.port",\
199 omero.constants.GLACIER2PORT)
200
201
202 router = id.properties.getProperty("Ice.Default.Router")
203 if not router or len(router) == 0:
204 router = str(omero.constants.DEFAULTROUTER)
205 host = id.properties.getPropertyWithDefault("omero.host", """<"omero.host" not set>""")
206 router = router.replace("@omero.port@", str(port))
207 router = router.replace("@omero.host@", str(host))
208 id.properties.setProperty("Ice.Default.Router", router)
209
210
211 dump = id.properties.getProperty("omero.dump")
212 if len(dump) > 0:
213 m = self.getPropertyMap(id.properties)
214 keys = list(m.keys())
215 keys.sort()
216 for key in keys:
217 print "%s=%s" % (key, m[key])
218
219 self.__lock.acquire()
220 try:
221 if self.__ic:
222 raise ClientError("Client already initialized")
223
224 self.__ic = Ice.initialize(id)
225
226 if not self.__ic:
227 raise ClientError("Improper initialization")
228
229
230 self.of = ObjectFactory()
231 self.of.registerObjectFactory(self.__ic)
232 for of in omero.rtypes.ObjectFactories.values():
233 of.register(self.__ic)
234
235
236 self.__uuid = str(uuid.uuid4())
237 ctx = self.__ic.getImplicitContext()
238 if not ctx:
239 raise ClientError("Ice.ImplicitContext not set to Shared")
240 ctx.put(omero.constants.CLIENTUUID, self.__uuid)
241
242
243 self.__oa = self.__ic.createObjectAdapter("omero.ClientCallback")
244 cb = BaseClient.CallbackI(self.__ic, self.__oa)
245 self.__oa.add(cb, self.__ic.stringToIdentity("ClientCallback/%s" % self.__uuid))
246 self.__oa.activate()
247 finally:
248 self.__lock.release()
249
251 """
252 Sets the omero.model.Session#getUserAgent() string for
253 this client. Every session creation will be passed this argument. Finding
254 open sesssions with the same agent can be done via
255 omero.api.ISessionPrx#getMyOpenAgentSessions(String).
256 """
257 self.__agent = agent
258
260 """
261 Specifies whether or not this client was created via a call to
262 createClient with a boolean of False. If insecure, then all
263 remote calls will use the insecure connection defined by the server.
264 """
265 return not self.__insecure
266
268 """
269 Creates a possibly insecure omero.client instance and calls joinSession
270 using the current getSessionId value. If secure is False, then first the
271 "omero.router.insecure" configuration property is retrieved from the server
272 and used as the value of "Ice.Default.Router" for the new client. Any exception
273 thrown during creation is passed on to the caller.
274 """
275 props = self.getPropertyMap()
276 if not secure:
277 insecure = self.getSession().getConfigService().getConfigValue("omero.router.insecure")
278 if insecure is not None and insecure != "":
279 props["Ice.Default.Router"] = insecure
280 else:
281 self.__logger.warn("Could not retrieve \"omero.router.insecure\"")
282
283 nClient = omero.client(props)
284 nClient.__insecure = not secure
285 nClient.setAgent("%s;secure=%s" % (self.__agent, secure))
286 nClient.joinSession(self.getSessionId())
287 return nClient
288
290 """
291 Calls closeSession() and ignores any exceptions.
292
293 Equivalent to close() in OmeroJava or omero::client::~client()
294 """
295 try:
296 self.closeSession()
297 except exceptions.Exception, e:
298 self.__logger.warning("Ignoring error in client.__del__:" + str(e.__class__))
299
301 """
302 Returns the Ice.Communicator for this instance or throws
303 an exception if None.
304 """
305 self.__lock.acquire()
306 try:
307 if not self.__ic:
308 raise ClientError("No Ice.Communicator active; call createSession() or create a new client instance")
309 return self.__ic
310 finally:
311 self.__lock.release()
312
314 """
315 Returns the Ice.ObjectAdapter for this instance or throws
316 an exception if None.
317 """
318 self.__lock.acquire()
319 try:
320 if not self.__oa:
321 raise ClientError("No Ice.ObjectAdapter active; call createSession() or create a new client instance")
322 return self.__oa
323 finally:
324 self.__lock.release()
325
327 """
328 Returns the current active session or throws an exception if none has been
329 created since the last closeSession()
330 """
331 self.__lock.acquire()
332 try:
333 sf = self.__sf
334 if not sf:
335 raise omero.ClientError("No session available")
336 return sf
337 finally:
338 self.__lock.release()
339
341 """
342 Returns the UUID for the current session without making a remote call.
343 Uses getSession() internally and will throw an exception if no session
344 is active.
345 """
346 return self.getSession().ice_getIdentity().name
347
349 """
350 Returns the Ice.ImplicitContext which defines what properties
351 will be sent on every method invocation.
352 """
353 return self.getCommunicator().getImplicitContext()
354
356 """
357 Returns the active properties for this instance
358 """
359 self.__lock.acquire()
360 try:
361 return self.__ic.getProperties()
362 finally:
363 self.__lock.release()
364
370
372 """
373 Returns all properties which are prefixed with "omero." or "Ice."
374 """
375 if properties is None:
376 properties = self.getProperties()
377
378 rv = dict()
379 for prefix in ["omero","Ice"]:
380 for k,v in properties.getPropertiesForPrefix(prefix).items():
381 rv[k] = v
382 return rv
383
385 """
386 Uses the given session uuid as name
387 and password to rejoin a running session
388 """
389 return self.createSession(session, session)
390
392 """
393 Performs the actual logic of logging in, which is done via the
394 getRouter(). Disallows an extant ServiceFactoryPrx, and
395 tries to re-create a null Ice.Communicator. A null or empty
396 username will throw an exception, but an empty password is allowed.
397 """
398 import omero
399
400 self.__lock.acquire()
401 try:
402
403
404
405 if self.__sf:
406 raise ClientError("Session already active. Create a new omero.client or closeSession()")
407
408 if not self.__ic:
409 if not self.__previous:
410 raise ClientError("No previous data to recreate communicator.")
411 self._initData(self.__previous)
412 self.__previous = None
413
414
415
416 if not username:
417 username = self.getProperty("omero.user")
418 elif isinstance(username,omero.RString):
419 username = username.val
420
421 if not username or len(username) == 0:
422 raise ClientError("No username specified")
423
424 if not password:
425 password = self.getProperty("omero.pass")
426 elif isinstance(password,omero.RString):
427 password = password.val
428
429 if not password:
430 raise ClientError("No password specified")
431
432
433 prx = None
434 retries = 0
435 while retries < 3:
436 reason = None
437 if retries > 0:
438 self.__logger.warning(\
439 "%s - createSession retry: %s"% (reason, retries) )
440 try:
441 ctx = dict(self.getImplicitContext().getContext())
442 ctx[omero.constants.AGENT] = self.__agent
443 prx = self.getRouter(self.__ic).createSession(username, password, ctx)
444 break
445 except omero.WrappedCreateSessionException, wrapped:
446 if not wrapped.concurrency:
447 raise wrapped
448 reason = "%s:%s" % (wrapped.type, wrapped.reason)
449 retries = retries + 1
450 except Ice.ConnectTimeoutException, cte:
451 reason = "Ice.ConnectTimeoutException:%s" % str(cte)
452 retries = retries + 1
453
454 if not prx:
455 raise ClientError("Obtained null object prox")
456
457
458 self.__sf = omero.api.ServiceFactoryPrx.uncheckedCast(prx)
459 if not self.__sf:
460 raise ClientError("Obtained object proxy is not a ServiceFactory")
461
462
463 keep_alive = self.__ic.getProperties().getPropertyWithDefault("omero.keep_alive", "-1")
464 try:
465 i = int(keep_alive)
466 self.enableKeepAlive(i)
467 except:
468 pass
469
470
471
472 try:
473 id = self.__ic.stringToIdentity("ClientCallback/%s" % self.__uuid )
474 raw = self.__oa.createProxy(id)
475 self.__sf.setCallback(omero.api.ClientCallbackPrx.uncheckedCast(raw))
476
477 except:
478 self.__del__()
479 raise
480
481
482 self.getImplicitContext().put(omero.constants.SESSIONUUID, self.getSessionId())
483
484 return self.__sf
485 finally:
486 self.__lock.release()
487
489 """
490 Resets the "omero.keep_alive" property on the current
491 Ice.Communicator which is used on initialization to determine
492 the time-period between Resource checks. If no __resources
493 instance is available currently, one is also created.
494 """
495
496 self.__lock.acquire()
497 try:
498
499 ic = self.getCommunicator()
500
501
502
503 ic.getProperties().setProperty("omero.keep_alive", str(seconds))
504
505
506
507 if self.__resources == None and seconds > 0:
508 self.__resources = omero.util.Resources(seconds)
509 class Entry:
510 def __init__(self, c):
511 self.c = c
512 def cleanup(self): pass
513 def check(self):
514 sf = self.c._BaseClient__sf
515 ic = self.c._BaseClient__ic
516 if sf != None:
517 try:
518 sf.keepAlive(None)
519 except exceptions.Exception, e:
520 if ic != None:
521 ic.getLogger().warning("Proxy keep alive failed.")
522 return False
523 return True
524 self.__resources.add(Entry(self))
525 finally:
526 self.__lock.release()
527
529 """
530 Acquires the default router, and throws an exception
531 if it is not of type Glacier2.Router. Also sets the
532 Ice.ImplicitContext on the router proxy.
533 """
534 prx = comm.getDefaultRouter()
535 if not prx:
536 raise ClientError("No default router found.")
537 router = Glacier2.RouterPrx.uncheckedCast(prx)
538 if not router:
539 raise ClientError("Error obtaining Glacier2 router")
540
541
542
543 router = router.ice_context(comm.getImplicitContext().getContext())
544 return router
545
546 - def sha1(self, filename):
547 """
548 Calculates the local sha1 for a file.
549 """
550 try:
551 from hashlib import sha1 as sha_new
552 except ImportError:
553 from sha import new as sha_new
554 digest = sha_new()
555 file = open(filename, 'rb')
556 try:
557 while True:
558 block = file.read(1024)
559 if not block:
560 break
561 digest.update(block)
562 finally:
563 file.close()
564 return digest.hexdigest()
565
566 - def upload(self, filename, name = None, path = None,
567 type = None, ofile = None, block_size = 1024):
635
636 - def download(self, ofile, filename = None, block_size = 1024*1024, filehandle = None):
637 prx = self.__sf.createRawFileStore()
638 try:
639 if not ofile or not ofile.id:
640 raise ClientError("No file to download")
641 ofile = self.__sf.getQueryService().get("OriginalFile", ofile.id.val)
642
643 if block_size > ofile.size.val:
644 block_size = ofile.size.val
645
646 prx.setFileId(ofile.id.val)
647
648 size = ofile.size.val
649 offset = 0
650
651 if filehandle is None:
652 if filename is None:
653 raise ClientError("no filename or filehandle specified")
654 filehandle = open(filename, 'wb')
655 else:
656 if filename:
657 raise ClientError("filename and filehandle specified.")
658
659 try:
660 while (offset+block_size) < size:
661 filehandle.write(prx.read(offset, block_size))
662 offset += block_size
663 filehandle.write(prx.read(offset, (size-offset)))
664 finally:
665 if filename:
666 filehandle.close()
667 finally:
668 prx.close()
669
671 """
672 Returns all active StatefulServiceInterface proxies. This can
673 be used to call close before calling setSecurityContext.
674 """
675 rv = []
676 sf = self.sf
677 services = sf.activeServices()
678 for srv in services:
679 try:
680 prx = sf.getByName(srv)
681 prx = omero.api.StatefulServiceInterfacePrx.checkedCast(prx)
682 if prx is not None:
683 rv.append(prx)
684 except:
685 self.__logger.warn("Error looking up proxy: %s" % srv, exc_info=1)
686 return rv
687
689 """
690 Closes the Router connection created by createSession(). Due to a bug in Ice,
691 only one connection is allowed per communicator, so we also destroy the communicator.
692 """
693
694 self.__lock.acquire()
695 try:
696 self.__sf = None
697
698 oldOa = self.__oa
699 self.__oa = None
700
701 oldIc = self.__ic
702 self.__ic = None
703
704
705 if not oldIc:
706 return
707
708 if oldOa:
709 try:
710 oldOa.deactivate()
711 except exceptions.Exception, e:
712 self.__logger.warning("While deactivating adapter: " + str(e.message))
713
714 self.__previous = Ice.InitializationData()
715 self.__previous.properties = oldIc.getProperties().clone()
716
717 oldR = self.__resources
718 self.__resources = None
719 if oldR != None:
720 try:
721 oldR.cleanup()
722 except exceptions.Exception, e:
723 oldIc.getLogger().warning(
724 "While cleaning up resources: " + str(e))
725
726 try:
727 try:
728 self.getRouter(oldIc).destroySession()
729 except Glacier2.SessionNotExistException:
730
731 pass
732 except Ice.ConnectionLostException:
733
734 pass
735 except Ice.ConnectionRefusedException:
736
737 pass
738 except Ice.ConnectTimeoutException:
739
740 pass
741
742
743 finally:
744 oldIc.destroy()
745 del oldIc._impl
746
747 finally:
748 self.__lock.release()
749
750
752 """
753 Calls ISession.closeSession(omero.model.Session) until
754 the returned reference count is greater than zero. The
755 number of invocations is returned. If ISession.closeSession()
756 cannot be called, -1 is returned.
757 """
758
759 s = omero.model.SessionI()
760 s.uuid = omero.rtypes.rstring(self.getSessionId())
761 try:
762 svc = self.sf.getSessionService()
763 except:
764 self.__logger.warning("Cannot get session service for killSession. Using closeSession")
765 self.closeSession()
766 return -1;
767
768 count = 0
769 try:
770 r = 1
771 while r > 0:
772 count += 1
773 r = svc.closeSession(s)
774 except omero.RemovedSessionException:
775 pass
776 except:
777 self.__logger.warning("Unknown exception while closing all references", exc_info = True)
778
779
780 self.closeSession()
781 return count
782
783
784
785
786 - def _env(self, _unwrap, method, *args):
787 """ Helper method to access session environment"""
788 session = self.getSession()
789 if not session:
790 raise ClientError("No session active")
791 a = session.getAdminService()
792 u = a.getEventContext().sessionUuid
793 s = session.getSessionService()
794 m = getattr(s, method)
795 rv = apply(m, (u,)+args)
796 if callable(_unwrap):
797 rv = _unwrap(rv)
798 elif _unwrap:
799 rv = omero.rtypes.unwrap(rv)
800 return rv
801
807
809 """
810 Retrieves an item from the "output" shared (session) memory.
811 """
812 return self._env(unwrap, "getOutput", key)
813
814
820
822 """
823 Sets an item in the "output" shared (session) memory under the given name.
824 """
825 self._env(False, "setOutput", key, value)
826
832
834 """
835 Returns a list of keys for all items in the "output" shared (session) memory
836 """
837 return self._env(False, "getOutputKeys")
838
844
846 """
847 Returns all items in the "output" shared (session) memory
848 """
849 return self._env(unwrap, "getOutputKeys")
850
851
852
853
855 currentValue = data.properties.getProperty(key)
856 if not currentValue or len(currentValue) == 0:
857 newStr = str(newValue)
858 data.properties.setProperty(key, newStr)
859 currentValue = newStr
860 return currentValue
861
863 """
864 Compatibility layer, which allows calls to getCommunicator() and getSession()
865 to be called via self.ic and self.sf
866 """
867 if name == "ic":
868 return self.getCommunicator()
869 elif name == "sf":
870 return self.getSession()
871 elif name == "adapter":
872 return self.getAdapter()
873 else:
874 raise AttributeError("Unknown property: " + name)
875
876
877
878
880 if not self.__oa:
881 raise ClientError("No session active; call createSession()")
882 obj = self.__oa.find(self.ic.stringToIdentity("ClientCallback/%s" % self.__uuid))
883 if not isinstance(obj, BaseClient.CallbackI):
884 raise ClientError("Cannot find CallbackI in ObjectAdapter")
885 return obj
886
889
892
895
897 """
898 Implemention of ClientCallback which will be added to
899 any Session which this instance creates. Note: this client
900 should avoid all interaction with the {@link client#lock} since it
901 can lead to deadlocks during shutdown. See: ticket:1210
902 """
903
904
905
906
910 try:
911 self.oa.deactivate();
912 except exceptions.Exception, e:
913 sys.err.write("On session closed: " + str(e))
914
921 - def execute(self, myCallable, action):
922 try:
923 myCallable()
924
925 except:
926 try:
927 self.ic.getLogger().error("Error performing %s" % action)
928 except:
929 print "Error performing %s" % action
930
933 - def shutdownIn(self, milliseconds, current = None):
937
938
939
940
941
942 import util.FactoryMap
944 """
945 Responsible for instantiating objects during deserialization.
946 """
947
950
952 for key in self.__m:
953 if not ic.findObjectFactory(key):
954 ic.addObjectFactory(self,key)
955
957 generator = self.__m[type]
958 if generator == None:
959 raise ClientError("Unknown type:"+type)
960 return generator.next()
961
965
966
967
969 """
970 Top of client exception hierarchy.
971 """
972 pass
973
976
979