Package omero :: Module config
[hide private]
[frames] | no frames]

Source Code for Module omero.config

  1  #!/usr/bin/env python 
  2  # encoding: utf-8 
  3  """ 
  4  :: 
  5   
  6     Copyright 2010 Glencoe Software, Inc. All rights reserved. 
  7     Use is subject to license terms supplied in LICENSE.txt 
  8   
  9  """ 
 10   
 11  """ 
 12  Module which parses an icegrid XML file for configuration settings. 
 13   
 14  see ticket:800 
 15  see ticket:2213 - Replacing Java Preferences API 
 16  """ 
 17   
 18  import re 
 19  import os 
 20  import path 
 21  import time 
 22  import logging 
 23  import exceptions 
 24  import portalocker 
 25   
 26  import xml.dom.minidom 
 27   
 28  try: 
 29      from xml.etree.ElementTree import XML, Element, SubElement, Comment, ElementTree, tostring 
 30  except ImportError: 
 31      from elementtree.ElementTree import XML, Element, SubElement, Comment, ElementTree, tostring 
 32   
 33   
34 -class ConfigXml(object):
35 """ 36 37 """ 38 KEY = "omero.config.version" 39 VERSION = "4.2.1" 40 INTERNAL = "__ACTIVE__" 41 DEFAULT = "omero.config.profile" 42 IGNORE = (KEY, DEFAULT) 43
44 - def __init__(self, filename, env_config = None, exclusive = True):
45 self.logger = logging.getLogger(self.__class__.__name__) #: Logs to the class name 46 self.XML = None #: Parsed XML Element 47 self.env_config = env_config #: Environment override 48 self.filename = filename #: Path to the file to be read and written 49 self.source = open(filename, "a+") #: Open file handle 50 self.lock = self._open_lock() #: Open file handle for lock 51 self.exclusive = exclusive #: Whether or not an exclusive lock should be acquired 52 try: 53 self._CloseEx = WindowsError 54 except NameError: 55 self._CloseEx = None 56 if exclusive: 57 try: 58 portalocker.lock(self.lock, portalocker.LOCK_NB|portalocker.LOCK_EX) 59 except portalocker.LockException, le: 60 self.lock = None # Prevent deleting of the file 61 self.close() 62 raise 63 64 self.source.seek(0) 65 text = self.source.read() 66 67 if text: 68 self.XML = XML(text) 69 try: 70 self.version_check() 71 except: 72 self.close() 73 raise 74 75 # Nothing defined, so create a new tree 76 if self.XML is None: 77 default = self.default() 78 self.XML = Element("icegrid") 79 properties = SubElement(self.XML, "properties", id=self.INTERNAL) 80 _ = SubElement(properties, "property", name=self.DEFAULT, value=default) 81 _ = SubElement(properties, "property", name=self.KEY, value=self.VERSION) 82 properties = SubElement(self.XML, "properties", id=default) 83 _ = SubElement(properties, "property", name=self.KEY, value=self.VERSION)
84
85 - def _open_lock(self):
86 return open("%s.lock" % self.filename, "a+")
87
88 - def _close_lock(self):
89 if self.lock is not None: 90 self.lock.close() 91 try: 92 os.remove("%s.lock" % self.filename) 93 except: 94 # On windows a WindowsError 32 can happen (file opened by another process), ignoring 95 self.logger.error("Failed to removed lock file, ignoring", exc_info=True) 96 pass
97
98 - def version(self, id = None):
99 if id is None: 100 id = self.default() 101 properties = self.properties(id) 102 if properties is not None: 103 for x in properties.getchildren(): 104 if x.get("name") == self.KEY: 105 return x.get("value")
106
107 - def version_check(self):
108 for k, v in self.properties(None, True): 109 version = self.version(k) 110 if version != self.VERSION: 111 self.version_fix(v, version)
112
113 - def version_fix(self, props, version):
114 """ 115 Currently we are assuming that all blocks without a 4.2.0 version 116 are bogus. The configuration script when it generates an initial 117 config.xml will use prefs.class to parse the existing values and 118 immediately do the upgrade. 119 """ 120 if version == "4.2.0": 121 # http://trac.openmicroscopy.org.uk/omero/ticket/2613 122 # Remove any reference to the ${omero.dollar} workaround 123 # then map anything of the form: ${...} to @{...} 124 if props: 125 for x in props.getchildren(): 126 if x.get("name", "").startswith("omero.ldap"): 127 orig = x.get("value", "") 128 val = orig.replace("${omero.dollar}", "") 129 val = val.replace("${", "@{") 130 x.set("value", val) 131 self.logger.info("Upgraded 4.2.0 property: %s => %s", orig, val) 132 else: 133 raise exceptions.Exception("Version mismatch: %s has %s" % (props.get("id"), version))
134
135 - def internal(self):
136 return self.properties(self.INTERNAL)
137
138 - def properties(self, id = None, filter_internal = False):
139 140 if self.XML is None: 141 return None 142 143 props = self.XML.findall("./properties") 144 if id is None: 145 rv = list() 146 for x in props: 147 id = x.attrib["id"] 148 if filter_internal: 149 if id == self.INTERNAL: 150 continue 151 rv.append((id, x)) 152 return rv 153 for p in props: 154 if "id" in p.attrib and p.attrib["id"] == id: 155 return p
156
157 - def remove(self, id = None):
158 if id is None: 159 id = self.default() 160 properties = self.properties(id) 161 if not properties: 162 raise KeyError("No such configuration: %s" % id) 163 self.XML.remove(properties)
164
165 - def default(self, value = None):
166 if value: 167 self.env_config = value 168 if self.env_config: 169 return self.env_config 170 elif "OMERO_CONFIG" in os.environ: 171 return os.environ["OMERO_CONFIG"] 172 else: 173 # Previously we were calling here: 174 # props = self.props_to_dict(self.internal()) 175 # return props.get(self.DEFAULT, "default") 176 # but we don't want to take the previous default 177 # because then a user would have to explicitly 178 # set: "OMERO_CONFIG=default" 179 return "default"
180
181 - def dump(self):
182 prop_list = self.properties() 183 for id, p in prop_list: 184 props = self.props_to_dict(p) 185 print "# ===> %s <===" % id 186 print self.dict_to_text(props)
187
188 - def save(self):
189 """ 190 Creates a fresh <icegrid> block (removing any unwanted 191 intra-element whitespace) and overwrites the file on disk. 192 """ 193 icegrid = Element("icegrid") 194 comment = Comment("\n".join(["\n", 195 "\tThis file was generated at %s by the OmeroConfig system.", 196 "\tDo not edit directly but see bin/omero config for details.", 197 "\tThis file may be included into your IceGrid application.", 198 "\n"]) % time.ctime()) 199 icegrid.append(comment) 200 # First step is to add a new self.INTERNAL block to it 201 # which has self.DEFAULT set to the current default, 202 # and then copies all the values from that profile. 203 default = self.default() 204 internal = SubElement(icegrid, "properties", id=self.INTERNAL) 205 SubElement(internal, "property", name=self.DEFAULT, value=default) 206 SubElement(internal, "property", name=self.KEY, value=self.VERSION) 207 to_copy = self.properties(default) 208 if to_copy is not None: 209 for x in to_copy.getchildren(): 210 if x.get("name") != self.DEFAULT and x.get("name") != self.KEY: 211 SubElement(internal, "property", x.attrib) 212 else: 213 # Doesn't exist, create it 214 properties = SubElement(icegrid, "properties", id=default) 215 SubElement(properties, "property", name=self.KEY, value=self.VERSION) 216 # Now we simply reproduce all the other blocks 217 prop_list = self.properties(None, True) 218 for k, p in prop_list: 219 self.clear_text(p) 220 icegrid.append(p) 221 self.source.seek(0) 222 self.source.truncate() 223 self.source.write(self.element_to_xml(icegrid)) 224 self.source.flush()
225
226 - def close(self):
227 try: 228 # If we didn't get an XML instance, 229 # then something has gone wrong and 230 # we should exit. 231 if self.XML is not None: 232 self.save() 233 finally: 234 try: 235 self.source.close() 236 finally: 237 self._close_lock()
238
239 - def props_to_dict(self, c):
240 241 if c is None: 242 return {} 243 244 rv = dict() 245 props = c.findall("./property") 246 for p in props: 247 if "name" in p.attrib: 248 rv[p.attrib["name"]] = p.attrib.get("value", "") 249 return rv
250
251 - def dict_to_text(self, parsed = None):
252 253 if parsed is None: 254 return 255 256 rv = "" 257 for k, v in parsed.items(): 258 rv += "%s=%s" % (k, v) 259 return rv
260
261 - def element_to_xml(self, elem):
262 string = tostring(elem, 'utf-8') 263 return xml.dom.minidom.parseString(string).toprettyxml(" ", "\n", None)
264
265 - def clear_text(self, p):
266 """ 267 To prevent the accumulation of text outside of elements (including whitespace) 268 we walk the given element and remove tail from it and it's children. 269 """ 270 p.tail = "" 271 p.text = "" 272 for p2 in p.getchildren(): 273 self.clear_text(p2)
274 275 # 276 # Map interface on the default properties element 277 #
278 - def as_map(self):
279 return self.props_to_dict(self.properties(self.default()))
280
281 - def keys(self):
282 return self.as_map().keys()
283
284 - def __getitem__(self, key):
285 return self.props_to_dict(self.properties(self.default()))[key]
286
287 - def __setitem__(self, key, value):
288 default = self.default() 289 props = self.properties(default) 290 291 if props == None: 292 props = SubElement(self.XML, "properties", {"id":default}) 293 294 for x in props.findall("./property"): 295 if x.attrib["name"] == key: 296 x.attrib["value"] = value 297 return 298 SubElement(props, "property", {"name":key, "value":value})
299
300 - def __delitem__(self, key):
301 default = self.default() 302 props = self.properties(default) 303 to_remove = [] 304 for p in props.getchildren(): 305 if p.get("name") == key: 306 to_remove.append(p) 307 for x in to_remove: 308 props.remove(x)
309