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 import exceptions, traceback, threading, logging
13 import Ice, Glacier2, Glacier2_Router_ice
14 import uuid
15 sys = __import__("sys")
16
17 api = __import__('omero.api')
18 model = __import__('omero.model')
19 util = __import__('omero.util')
20 sys = __import__('omero.sys')
21
22 import omero_API_ice
23 import omero_Constants_ice
24 import omero_sys_ParametersI
25 import omero.rtypes
26 from omero.rtypes import rlong
27 from omero.rtypes import rint
28 from omero.rtypes import rstring
29 from omero.rtypes import rdouble
30 from omero.rtypes import rfloat
31
32 finally:
33 __name__ = __save__
34 del __save__
35
36 ClientError = omero.ClientError
37
39 """
40 Central client-side blitz entry point, and should be in sync with OmeroJava's omero.client
41 and OmeroCpp's omero::client.
42
43 Typical usage includes::
44
45 client = omero.client() # Uses --Ice.Config argument or ICE_CONFIG variable
46 client = omero.client(host = host) # Defines "omero.host"
47 client = omero.client(host = host, port = port) # Defines "omero.host" and "omero.port"
48
49 For more information, see:
50
51 - U{https://trac.openmicroscopy.org.uk/omero/wiki/ClientDesign}
52
53 """
54
55 - def __init__(self, args = None, id = None, \
56 host = None, port = None, pmap = None):
57 """
58 Constructor which takes one sys.argv-style list, one initialization
59 data, one host string, one port integer, and one properties map, in
60 that order. *However*, to simplify use, we reassign values based on
61 their type with a warning printed. A cleaner approach is to use named
62 parameters.
63 ::
64 c1 = omero.client(None, None, "host", myPort) # Correct
65 c2 = omero.client(host = "host", port = myPort) # Correct
66 c3 = omero.client("host", myPort) # Works with warning
67
68 Both "Ice" and "omero" prefixed properties will be parsed.
69
70 Defines the state variables::
71 __previous : InitializationData from any previous communicator, if any
72 Used to re-initialization the client post-closeSession()
73
74 __ic : communicator. Nullness => init() needed on createSession()
75
76 __sf : current session. Nullness => createSession() needed.
77
78 __resources: if non-null, hs access to this client instance and will
79 periodically call sf.keepAlive(None) in order to keep any
80 session alive. This can be enabled either via the omero.keep_alive
81 configuration property, or by calling the enableKeepAlive() method.
82 Once enabled, the period cannot be adjusted during a single
83 session.
84
85 Modifying these variables outside of the accessors can lead to
86 undefined behavior.
87
88 Equivalent to all OmeroJava and OmeroCpp constructors
89 """
90
91
92 self.__previous = None
93 self.__ic = None
94 self.__oa = None
95 self.__sf = None
96 self.__uuid = None
97 self.__resources = None
98 self.__lock = threading.RLock()
99
100
101 self.__logger = logging.getLogger("omero.client")
102 logging.basicConfig()
103
104
105
106 args, id, host, port, pmap = self._repair(args, id, host, port, pmap)
107
108
109 if not args:
110 args = []
111 else:
112 args = list(args)
113
114
115 if id == None:
116 id = Ice.InitializationData()
117
118 if id.properties == None:
119 id.properties = Ice.createProperties(args)
120
121 id.properties.parseCommandLineOptions("omero", args);
122 if host:
123 id.properties.setProperty("omero.host", str(host))
124 if not port:
125 port = id.properties.getPropertyWithDefault("omero.port",\
126 str(omero.constants.GLACIER2PORT))
127 id.properties.setProperty("omero.port", str(port))
128 if pmap:
129 for k,v in pmap.items():
130 id.properties.setProperty(str(k), str(v))
131
132 self._initData(id)
133
134 - def _repair(self, args, id, host, port, pmap):
135 """
136 Takes the 5 arguments passed to the __init__ method
137 and attempts to re-order them based on their types.
138 This allows for simplified usage without parameter
139 names.
140 """
141 types = [list, Ice.InitializationData, str, int, dict]
142 original = [args, id, host, port, pmap]
143 repaired = [None, None, None, None, None]
144
145
146 valid = True
147 for i in range(0, len(types)):
148 if None != original[i] and not isinstance(original[i], types[i]):
149 valid = False
150 break
151 if valid:
152 return original
153
154
155 for i in range(0, len(types)):
156 found = None
157 for j in range(0, len(types)):
158 if isinstance(original[j], types[i]):
159 if not found:
160 found = original[j]
161 else:
162 raise ClientError("Found two arguments of same type: " + str(types[i]))
163 if found:
164 repaired[i] = found
165 return repaired
166
168 """
169 Initializes the current client via an Ice.InitializationData
170 instance. This is called by all of the constructors, but may
171 also be called on createSession(name, pass) if a previous
172 call to closeSession() has nulled the Ice.Communicator.
173 """
174
175 if not id:
176 raise ClientError("No initialization data provided.");
177
178
179 id.properties.setProperty("Ice.ImplicitContext", "Shared")
180 id.properties.setProperty("Ice.ACM.Client", "0")
181 id.properties.setProperty("Ice.RetryIntervals", "-1")
182
183
184 messageSize = id.properties.getProperty("Ice.MessageSizeMax")
185 if not messageSize or len(messageSize) == 0:
186 id.properties.setProperty("Ice.MessageSizeMax", str(omero.constants.MESSAGESIZEMAX))
187
188
189 self.parseAndSetInt(id, "Ice.Override.ConnectTimeout",\
190 omero.constants.CONNECTTIMEOUT)
191
192
193 endpoints = id.properties.getProperty("omero.ClientCallback.Endpoints")
194 if not endpoints or len(endpoints) == 0:
195 id.properties.setProperty("omero.ClientCallback.Endpoints", "tcp")
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 for prefix in ["omero","Ice"]:
214 for k,v in id.properties.getPropertiesForPrefix(prefix).items():
215 print "%s=%s" % (k,v)
216
217 self.__lock.acquire()
218 try:
219 if self.__ic:
220 raise ClientError("Client already initialized")
221
222 self.__ic = Ice.initialize(id)
223
224 if not self.__ic:
225 raise ClientError("Improper initialization")
226
227
228 self.of = ObjectFactory()
229 self.of.registerObjectFactory(self.__ic)
230 for of in omero.rtypes.ObjectFactories.values():
231 of.register(self.__ic)
232
233
234 self.__uuid = str(uuid.uuid4())
235 ctx = self.__ic.getImplicitContext()
236 if not ctx:
237 raise ClientError("Ice.ImplicitContext not set to Shared")
238 ctx.put(omero.constants.CLIENTUUID, self.__uuid)
239
240
241 self.__oa = self.__ic.createObjectAdapter("omero.ClientCallback")
242 cb = BaseClient.CallbackI(self.__ic, self.__oa)
243 self.__oa.add(cb, self.__ic.stringToIdentity("ClientCallback/%s" % self.__uuid))
244 self.__oa.activate()
245 finally:
246 self.__lock.release()
247
249 """
250 Calls closeSession() and ignores any exceptions.
251
252 Equivalent to close() in OmeroJava or omero::client::~client()
253 """
254 try:
255 self.closeSession()
256 except exceptions.Exception, e:
257 self.__logger.warning("Ignoring error in client.__del__:" + str(e.__class__))
258
260 """
261 Returns the Ice.Communicator for this instance or throws
262 an exception if None.
263 """
264 self.__lock.acquire()
265 try:
266 if not self.__ic:
267 raise ClientError("No Ice.Communicator active; call createSession() or create a new client instance")
268 return self.__ic
269 finally:
270 self.__lock.release()
271
273 """
274 Returns the Ice.ObjectAdapter for this instance or throws
275 an exception if None.
276 """
277 self.__lock.acquire()
278 try:
279 if not self.__oa:
280 raise ClientError("No Ice.ObjectAdapter active; call createSession() or create a new client instance")
281 return self.__oa
282 finally:
283 self.__lock.release()
284
286 """
287 Returns the current active session or throws an exception if none has been
288 created since the last closeSession()
289 """
290 self.__lock.acquire()
291 try:
292 return self.__sf
293 finally:
294 self.__lock.release()
295
297 """
298 Returns the Ice.ImplicitContext which defines what properties
299 will be sent on every method invocation.
300 """
301 return self.getCommunicator().getImplicitContext()
302
304 """
305 Returns the active properties for this instance
306 """
307 self.__lock.acquire()
308 try:
309 return self.__ic.getProperties()
310 finally:
311 self.__lock.release()
312
314 """
315 Returns the property for the given key or "" if none present
316 """
317 return self.getProperties().getProperty(key)
318
320 """
321 Uses the given session uuid as name
322 and password to rejoin a running session
323 """
324 return self.createSession(session, session)
325
327 """
328 Performs the actual logic of logging in, which is done via the
329 getRouter(). Disallows an extant ServiceFactoryPrx, and
330 tries to re-create a null Ice.Communicator. A null or empty
331 username will throw an exception, but an empty password is allowed.
332 """
333 import omero
334
335 self.__lock.acquire()
336 try:
337
338
339
340 if self.__sf:
341 raise ClientError("Session already active. Create a new omero.client or closeSession()")
342
343 if not self.__ic:
344 if not self.__previous:
345 raise ClientError("No previous data to recreate communicator.")
346 self._initData(self.__previous)
347 self.__previous = None
348
349
350
351 if not username:
352 username = self.getProperty("omero.user")
353 elif isinstance(username,omero.RString):
354 username = username.val
355
356 if not username or len(username) == 0:
357 raise ClientError("No username specified")
358
359 if not password:
360 password = self.getProperty("omero.pass")
361 elif isinstance(password,omero.RString):
362 password = password.val
363
364 if not password:
365 raise ClientError("No password specified")
366
367
368 prx = None
369 retries = 0
370 while retries < 3:
371 reason = None
372 if retries > 0:
373 self.__logger.warning(\
374 "%s - createSession retry: %s"% (reason, retries) )
375 try:
376 prx = self.getRouter(self.__ic).createSession(username, password)
377 break
378 except omero.WrappedCreateSessionException, wrapped:
379 if not wrapped.concurrency:
380 raise wrapped
381 reason = "%s:%s" % (wrapped.type, wrapped.reason)
382 retries = retries + 1
383 except Ice.ConnectTimeoutException, cte:
384 reason = "Ice.ConnectTimeoutException:%s" % str(cte)
385 retries = retries + 1
386
387 if not prx:
388 raise ClientError("Obtained null object prox")
389
390
391 self.__sf = omero.api.ServiceFactoryPrx.uncheckedCast(prx)
392 if not self.__sf:
393 raise ClientError("Obtained object proxy is not a ServiceFactory")
394
395
396 keep_alive = self.__ic.getProperties().getPropertyWithDefault("omero.keep_alive", "-1")
397 try:
398 i = int(keep_alive)
399 self.enableKeepAlive(i)
400 except:
401 pass
402
403
404
405 id = self.__ic.stringToIdentity("ClientCallback/%s" % self.__uuid )
406 raw = self.__oa.createProxy(id)
407 self.__sf.setCallback(omero.api.ClientCallbackPrx.uncheckedCast(raw))
408
409
410 return self.__sf
411 finally:
412 self.__lock.release()
413
415 """
416 Resets the "omero.keep_alive" property on the current
417 Ice.Communicator which is used on initialization to determine
418 the time-period between Resource checks. If no __resources
419 instance is available currently, one is also created.
420 """
421
422 self.__lock.acquire()
423 try:
424
425 ic = self.getCommunicator()
426
427
428
429 ic.getProperties().setProperty("omero.keep_alive", str(seconds))
430
431
432
433 if self.__resources == None and seconds > 0:
434 self.__resources = omero.util.Resources(seconds)
435 class Entry:
436 def __init__(self, c):
437 self.c = c
438 def cleanup(self): pass
439 def check(self):
440 sf = self.c._BaseClient__sf
441 ic = self.c._BaseClient__ic
442 if sf != None:
443 try:
444 sf.keepAlive(None)
445 except exceptions.Exception, e:
446 self.c._BaseClient__logger.warning("Proxy keep alive failed.")
447 return False
448 return True
449 self.__resources.add(Entry(self))
450 finally:
451 self.__lock.release()
452
454 """
455 Acquires the default router, and throws an exception
456 if it is not of type Glacier2.Router. Also sets the
457 Ice.ImplicitContext on the router proxy.
458 """
459 prx = comm.getDefaultRouter()
460 if not prx:
461 raise ClientError("No default router found.")
462 router = Glacier2.RouterPrx.uncheckedCast(prx)
463 if not router:
464 raise ClientError("Error obtaining Glacier2 router")
465
466
467
468 router = router.ice_context(comm.getImplicitContext().getContext())
469 return router
470
471 - def sha1(self, filename):
472 """
473 Calculates the local sha1 for a file.
474 """
475 import sha
476 digest = sha.new()
477 file = open(filename, 'rb')
478 try:
479 while True:
480 block = file.read(1024)
481 if not block:
482 break
483 digest.update(block)
484 finally:
485 file.close()
486 return digest.hexdigest()
487
488 - def upload(self, filename, name = None, path = None,
489 type = None, ofile = None, block_size = 1024,
490 permissions = None):
491 """
492 Utility method to upload a file to the server.
493 """
494 if not self.__sf:
495 raise ClientError("No session. Use createSession first.")
496
497 import os, types
498 if not filename or not isinstance(filename, types.StringType):
499 raise ClientError("Non-null filename must be provided")
500
501 if not os.path.exists(filename):
502 raise ClientError("File does not exist: " + filename)
503
504 file = open(filename, 'rb')
505 try:
506
507 size = os.path.getsize(file.name)
508 if block_size > size:
509 block_size = size
510
511 if not ofile:
512 ofile = omero.model.OriginalFileI()
513
514 ofile.size = rlong(size)
515 ofile.sha1 = rstring(self.sha1(file.name))
516
517 if not ofile.name:
518 if name:
519 ofile.name = rstring(name)
520 else:
521 ofile.name = rstring(file.name)
522
523 if not ofile.path:
524 ofile.path = rstring(os.path.abspath(file.name))
525
526 if not ofile.format:
527 if not type:
528
529
530 raise ClientError("no format given")
531 else:
532 ofile.format = omero.model.FormatI()
533 ofile.format.value = rstring(type)
534
535 if permissions:
536 ofile.details.permissions = permissions
537
538 up = self.__sf.getUpdateService()
539 ofile = up.saveAndReturnObject(ofile)
540
541 prx = self.__sf.createRawFileStore()
542 prx.setFileId(ofile.id.val)
543 offset = 0
544 while True:
545 block = file.read(block_size)
546 if not block:
547 break
548 prx.write(block, offset, len(block))
549 offset += len(block)
550 prx.close()
551 finally:
552 file.close()
553
554 return ofile
555
556 - def download(self, ofile, filename, block_size = 1024*1024):
557 file = open(filename, 'wb')
558 try:
559 prx = self.__sf.createRawFileStore()
560 try:
561 if not ofile or not ofile.id:
562 raise ClientError("No file to download")
563 ofile = self.__sf.getQueryService().get("OriginalFile", ofile.id.val)
564
565 if block_size > ofile.size.val:
566 block_size = ofile.size.val
567
568 prx.setFileId(ofile.id.val)
569 offset = 0
570 while offset < ofile.size.val:
571 block = prx.read(offset, block_size)
572 if not block:
573 break
574 file.write(block)
575 offset += len(block)
576 file.flush()
577 finally:
578 prx.close()
579 finally:
580 file.close()
581
583 """
584 Closes the Router connection created by createSession(). Due to a bug in Ice,
585 only one connection is allowed per communicator, so we also destroy the communicator.
586 """
587
588 self.__lock.acquire()
589 try:
590 oldSf = self.__sf
591 self.__sf = None
592
593 oldOa = self.__oa
594 self.__oa = None
595
596 oldIc = self.__ic
597 self.__ic = None
598
599
600 if not oldIc:
601 return
602
603 if oldOa:
604 try:
605 oldOa.deactivate()
606 except exceptions.Exception, e:
607 self.__logger.warning("While deactivating adapter: " + str(e.message))
608
609 self.__previous = Ice.InitializationData()
610 self.__previous.properties = oldIc.getProperties().clone()
611
612 oldR = self.__resources
613 self.__resources = None
614 if oldR != None:
615 try:
616 oldR.cleanup()
617 except exceptions.Exception, e:
618 self.__logger.warning(
619 "While cleaning up resources: " + str(e))
620
621 try:
622 try:
623 if oldSf:
624 oldSf = omero.api.ServiceFactoryPrx.uncheckedCast(oldSf.ice_oneway())
625 oldSf.destroy()
626 except Glacier2.SessionNotExistException:
627
628 pass
629 except Ice.ConnectionLostException:
630
631 pass
632 except Ice.ConnectionRefusedException:
633
634 pass
635 except Ice.ConnectTimeoutException:
636
637 pass
638 finally:
639 oldIc.destroy()
640
641 finally:
642 self.__lock.release()
643
644
646 """
647 Calls closeSession(omero.model.Session) until
648 the returned reference count is greater than zero.
649 """
650
651 sf = self.sf
652 if not sf:
653 raise ClientError("No session avaliable")
654
655 s = omero.model.SessionI()
656 s.uuid = rstring(sf.ice_getIdentity().name)
657 try:
658 svc = sf.getSessionService()
659 except:
660 self.__logger.warning("Cannot get session service for killSession. Using closeSession")
661 self.closeSession()
662 return
663 count = 0
664 try:
665 r = 1
666 while r > 0:
667 count += 1
668 r = svc.closeSession(s)
669 except omero.RemovedSessionException:
670 pass
671 except:
672 self.__logger.warning("Unknown exception while closing all references", exc_info = True)
673
674
675 self.closeSession()
676 return count
677
678
679
680
681 - def _env(self, method, *args):
691
697
699 """
700 Retrieves an item from the "output" shared (session) memory.
701 """
702 return self._env("getOutput", key)
703
704
710
712 """
713 Sets an item in the "output" shared (session) memory under the given name.
714 """
715 self._env("setOutput", key, value)
716
722
724 """
725 Returns a list of keys for all items in the "output" shared (session) memory
726 """
727 return self._env("getOutputKeys")
728
729
730
731
732
734 currentValue = data.properties.getProperty(key)
735 if not currentValue or len(currentValue) == 0:
736 newStr = str(newValue)
737 data.properties.setProperty(key, newStr)
738 currentValue = newStr
739 return currentValue
740
742 """
743 Compatibility layer, which allows calls to getCommunicator() and getSession()
744 to be called via self.ic and self.sf
745 """
746 if name == "ic":
747 return self.getCommunicator()
748 elif name == "sf":
749 return self.getSession()
750 else:
751 raise AttributeError("Unknown property: " + name)
752
753
754
755
757 if not self.__oa:
758 raise ClientError("No session active; call createSession()")
759 obj = self.__oa.find(self.ic.stringToIdentity("ClientCallback/%s" % self.__uuid))
760 if not isinstance(obj, BaseClient.CallbackI):
761 raise ClientError("Cannot find CallbackI in ObjectAdapter")
762 return obj
763
766
769
772
774 """
775 Implemention of ClientCallback which will be added to
776 any Session which this instance creates. Note: this client
777 should avoid all interaction with the {@link client#lock} since it
778 can lead to deadlocks during shutdown. See: ticket:1210
779 """
780
781
782
783
787 try:
788 self.oa.deactivate();
789 except exceptions.Exception, e:
790 sys.err.write("On session closed: " + str(e))
791
798 - def execute(self, myCallable, action):
799 try:
800 myCallable()
801 self.__logger.debug("ClientCallback %s run", action)
802 except:
803 try:
804 self.__logger.error("Error performing %s" % action)
805 except:
806 print "Error performing %s" % action
807
810 - def shutdownIn(self, milliseconds, current = None):
814
815
816
817
818
819 import util.FactoryMap
821 """
822 Responsible for instantiating objects during deserialization.
823 """
824
827
829 for key in self.__m:
830 if not ic.findObjectFactory(key):
831 ic.addObjectFactory(self,key)
832
834 generator = self.__m[type]
835 if generator == None:
836 raise ClientError("Unknown type:"+type)
837 return generator.next()
838
842