support OpenOffice.org 3.4 by using -opt instead of --opt.
[cloudooo.git] / cloudooo / handler / ooo / application / openoffice.py
1 ##############################################################################
2 #
3 # Copyright (c) 2009-2010 Nexedi SA and Contributors. All Rights Reserved.
4 # Gabriel M. Monnerat <gabriel@tiolive.com>
5 #
6 # WARNING: This program as such is intended to be used by professional
7 # programmers who take the whole responsibility of assessing all potential
8 # consequences resulting from its eventual inadequacies and bugs
9 # End users who are looking for a ready-to-use solution with commercial
10 # guarantees and support are strongly adviced to contract a Free Software
11 # Service Company
12 #
13 # This program is Free Software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; either version 2
16 # of the License, or (at your option) any later version.
17 #
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
22 #
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26 #
27 ##############################################################################
28
29 import pkg_resources
30 import psutil
31 from os.path import exists, join
32 from subprocess import Popen, PIPE
33 from threading import Lock
34 from zope.interface import implements
35 from application import Application
36 from cloudooo.interfaces.lockable import ILockable
37 from cloudooo.util import logger, convertStringToBool
38 from cloudooo.handler.ooo.util import waitStartDaemon, \
39 removeDirectory, waitStopDaemon, \
40 socketStatus
41
42
43 class OpenOffice(Application):
44 """Object to control one OOo Instance and all features instance."""
45
46 implements(ILockable)
47
48 name = "openoffice"
49
50 def __init__(self):
51 """Creates the variable to save the pid, port and hostname of the object.
52 The lock is a simple lock python that is used when one requisition is
53 using the openoffice.
54 """
55 self._bin_soffice = 'soffice.bin'
56 self._lock = Lock()
57 self._cleanRequest()
58
59 def _testOpenOffice(self, host, port):
60 """Test if OpenOffice was started correctly"""
61 logger.debug("Test OpenOffice %s - Pid %s" % (self.getAddress()[-1],
62 self.pid()))
63 python = join(self.office_binary_path, "python")
64 args = [exists(python) and python or "python",
65 pkg_resources.resource_filename("cloudooo",
66 join('handler', 'ooo',
67 "helper", "openoffice_tester.py")),
68 "--hostname=%s" % host,
69 "--port=%s" % port,
70 "--uno_path=%s" % self.uno_path]
71 logger.debug("Testing Openoffice Instance %s" % port)
72 stdout, stderr = Popen(args, stdout=PIPE,
73 stderr=PIPE, close_fds=True).communicate()
74 stdout_bool = convertStringToBool(stdout.replace("\n", ""))
75 if stdout_bool and stderr != "":
76 logger.debug("%s\n%s" % (stderr, stdout))
77 return False
78 else:
79 logger.debug("Instance %s works" % port)
80 return True
81
82 def _cleanRequest(self):
83 """Define request attribute as 0"""
84 self.request = 0
85
86 def loadSettings(self, hostname, port, path_run_dir,
87 office_binary_path, uno_path, default_language,
88 environment_dict=None, **kw):
89 """Method to load the configuratio to control one OpenOffice Instance
90 Keyword arguments:
91 office_path -- Full Path of the OOo executable.
92 e.g office_binary_path='/opt/openoffice.org3/program'
93 uno_path -- Full path of the Uno Library
94 """
95 if environment_dict is None:
96 environment_dict = {}
97 Application.loadSettings(self, hostname, port, path_run_dir)
98 self.office_binary_path = office_binary_path
99 self.uno_path = uno_path
100 self.default_language = default_language
101 self.environment_dict = environment_dict
102
103 def _startProcess(self, command, env):
104 """Start OpenOffice.org process"""
105 for i in range(5):
106 self.stop()
107 waitStopDaemon(self, self.timeout)
108 self.process = Popen(command,
109 close_fds=True,
110 env=env)
111 if not waitStartDaemon(self, self.timeout):
112 continue
113 if self._testOpenOffice(self.hostname, self.port):
114 return
115
116 def _releaseOpenOfficePort(self):
117 for process in psutil.process_iter():
118 try:
119 if process.exe == join(self.office_binary_path, self._bin_soffice):
120 for connection in process.get_connections():
121 if connection.status == "LISTEN" and \
122 connection.local_address[1] == self.port:
123 process.terminate()
124 except psutil.error.AccessDenied, e:
125 pass
126 except TypeError, e:
127 # exception to prevent one psutil issue with zombie processes
128 logger.debug(e)
129 except NotImplementedError, e:
130 logger.error("lsof isn't installed on this machine: " + str(e))
131
132 def start(self):
133 """Start Instance."""
134 self.path_user_installation = join(self.path_run_dir, \
135 "cloudooo_instance_%s" % self.port)
136 if exists(self.path_user_installation):
137 removeDirectory(self.path_user_installation)
138 # Create command with all parameters to start the instance
139 self.command = [join(self.office_binary_path, self._bin_soffice),
140 '-headless',
141 '-invisible',
142 '-nocrashreport',
143 '-nologo',
144 '-nodefault',
145 '-norestore',
146 '-nofirststartwizard',
147 '-accept=socket,host=%s,port=%d;urp;' % (self.hostname, self.port),
148 '-env:UserInstallation=file://%s' % self.path_user_installation,
149 '-language=%s' % self.default_language,
150 ]
151 # To run soffice.bin, several environment variables should be set.
152 env = self.environment_dict.copy()
153 env["LANG"] = "UTF-8"
154 env["HOME"] = self.path_user_installation
155 env["TMP"] = self.path_user_installation
156 env["TMPDIR"] = self.path_user_installation
157 self._startProcess(self.command, env)
158 self._cleanRequest()
159 Application.start(self)
160
161 def stop(self):
162 """Stop the instance by pid. By the default
163 the signal is 15."""
164 Application.stop(self)
165 if socketStatus(self.hostname, self.port):
166 self._releaseOpenOfficePort()
167 self._cleanRequest()
168
169 def isLocked(self):
170 """Verify if OOo instance is being used."""
171 return self._lock.locked()
172
173 def acquire(self):
174 """Lock Instance to use."""
175 self.request += 1
176 self._lock.acquire()
177
178 def release(self):
179 """Unlock Instance."""
180 logger.debug("OpenOffice %s, %s unlocked" % self.getAddress())
181 self._lock.release()
182
183 openoffice = OpenOffice()