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 if ic != None:
447 self.__logger.warning("Proxy keep alive failed.")
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):
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 finally:
577 prx.close()
578 finally:
579 file.close()
580
582 """
583 Closes the Router connection created by createSession(). Due to a bug in Ice,
584 only one connection is allowed per communicator, so we also destroy the communicator.
585 """
586
587 self.__lock.acquire()
588 try:
589 self.__sf = None
590
591 oldOa = self.__oa
592 self.__oa = None
593
594 oldIc = self.__ic
595 self.__ic = None
596
597
598 if not oldIc:
599 return
600
601 if oldOa:
602 try:
603 oldOa.deactivate()
604 except exceptions.Exception, e:
605 self.__logger.warning("While deactivating adapter: " + str(e.message))
606
607 self.__previous = Ice.InitializationData()
608 self.__previous.properties = oldIc.getProperties().clone()
609
610 oldR = self.__resources
611 self.__resources = None
612 if oldR != None:
613 try:
614 oldR.cleanup()
615 except exceptions.Exception, e:
616 self.__logger.warning(
617 "While cleaning up resources: " + str(e))
618
619 try:
620 try:
621 self.getRouter(oldIc).destroySession()
622 except Glacier2.SessionNotExistException:
623
624 pass
625 except Ice.ConnectionLostException:
626
627 pass
628 except Ice.ConnectionRefusedException:
629
630 pass
631 except Ice.ConnectTimeoutException:
632
633 pass
634 finally:
635 oldIc.destroy()
636
637 finally:
638 self.__lock.release()
639
640
642 """
643 Calls closeSession(omero.model.Session) until
644 the returned reference count is greater than zero.
645 """
646
647 sf = self.sf
648 if not sf:
649 raise ClientError("No session avaliable")
650
651 s = omero.model.SessionI()
652 s.uuid = rstring(sf.ice_getIdentity().name)
653 try:
654 svc = sf.getSessionService()
655 except:
656 self.__logger.warning("Cannot get session service for killSession. Using closeSession")
657 self.closeSession()
658 return
659 count = 0
660 try:
661 r = 1
662 while r > 0:
663 count += 1
664 r = svc.closeSession(s)
665 except omero.RemovedSessionException:
666 pass
667 except:
668 self.__logger.warning("Unknown exception while closing all references", exc_info = True)
669
670
671 self.closeSession()
672 return count
673
674
675
676
677 - def _env(self, method, *args):
687
693
695 """
696 Retrieves an item from the "output" shared (session) memory.
697 """
698 return self._env("getOutput", key)
699
700
706
708 """
709 Sets an item in the "output" shared (session) memory under the given name.
710 """
711 self._env("setOutput", key, value)
712
718
720 """
721 Returns a list of keys for all items in the "output" shared (session) memory
722 """
723 return self._env("getOutputKeys")
724
725
726
727
728
730 currentValue = data.properties.getProperty(key)
731 if not currentValue or len(currentValue) == 0:
732 newStr = str(newValue)
733 data.properties.setProperty(key, newStr)
734 currentValue = newStr
735 return currentValue
736
738 """
739 Compatibility layer, which allows calls to getCommunicator() and getSession()
740 to be called via self.ic and self.sf
741 """
742 if name == "ic":
743 return self.getCommunicator()
744 elif name == "sf":
745 return self.getSession()
746 else:
747 raise AttributeError("Unknown property: " + name)
748
749
750
751
753 if not self.__oa:
754 raise ClientError("No session active; call createSession()")
755 obj = self.__oa.find(self.ic.stringToIdentity("ClientCallback/%s" % self.__uuid))
756 if not isinstance(obj, BaseClient.CallbackI):
757 raise ClientError("Cannot find CallbackI in ObjectAdapter")
758 return obj
759
762
765
768
770 """
771 Implemention of ClientCallback which will be added to
772 any Session which this instance creates. Note: this client
773 should avoid all interaction with the {@link client#lock} since it
774 can lead to deadlocks during shutdown. See: ticket:1210
775 """
776
777
778
779
783 try:
784 self.oa.deactivate();
785 except exceptions.Exception, e:
786 sys.err.write("On session closed: " + str(e))
787
794 - def execute(self, myCallable, action):
795 try:
796 myCallable()
797 self.__logger.debug("ClientCallback %s run", action)
798 except:
799 try:
800 self.__logger.error("Error performing %s" % action)
801 except:
802 print "Error performing %s" % action
803
806 - def shutdownIn(self, milliseconds, current = None):
810
811
812
813
814
815 import util.FactoryMap
817 """
818 Responsible for instantiating objects during deserialization.
819 """
820
823
825 for key in self.__m:
826 if not ic.findObjectFactory(key):
827 ic.addObjectFactory(self,key)
828
830 generator = self.__m[type]
831 if generator == None:
832 raise ClientError("Unknown type:"+type)
833 return generator.next()
834
838