Return-Path: X-Original-To: apmail-incubator-ooo-commits-archive@minotaur.apache.org Delivered-To: apmail-incubator-ooo-commits-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 187E177E6 for ; Sun, 27 Nov 2011 23:07:56 +0000 (UTC) Received: (qmail 89232 invoked by uid 500); 27 Nov 2011 23:07:56 -0000 Delivered-To: apmail-incubator-ooo-commits-archive@incubator.apache.org Received: (qmail 89202 invoked by uid 500); 27 Nov 2011 23:07:56 -0000 Mailing-List: contact ooo-commits-help@incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: ooo-dev@incubator.apache.org Delivered-To: mailing list ooo-commits@incubator.apache.org Received: (qmail 89195 invoked by uid 99); 27 Nov 2011 23:07:55 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 27 Nov 2011 23:07:55 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 27 Nov 2011 23:07:47 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id ED92A23889B3; Sun, 27 Nov 2011 23:07:24 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1206906 [1/3] - in /incubator/ooo/ooo-site/trunk/content/udk/python: ./ download/ images/ oood/ samples/ scriptingframework/ Date: Sun, 27 Nov 2011 23:07:23 -0000 To: ooo-commits@incubator.apache.org From: kschenk@apache.org X-Mailer: svnmailer-1.0.8-patched Message-Id: <20111127230724.ED92A23889B3@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: kschenk Date: Sun Nov 27 23:07:13 2011 New Revision: 1206906 URL: http://svn.apache.org/viewvc?rev=1206906&view=rev Log: kls -- added usk/python Added: incubator/ooo/ooo-site/trunk/content/udk/python/ incubator/ooo/ooo-site/trunk/content/udk/python/download/ incubator/ooo/ooo-site/trunk/content/udk/python/download/pyuno-linux-x86-0.9.2.tar.gz (with props) incubator/ooo/ooo-site/trunk/content/udk/python/download/pyuno-linux-x86-0.9.3.tar.gz (with props) incubator/ooo/ooo-site/trunk/content/udk/python/download/pyuno-win32-0.9.2.zip (with props) incubator/ooo/ooo-site/trunk/content/udk/python/download/pyuno-win32-0.9.3.zip (with props) incubator/ooo/ooo-site/trunk/content/udk/python/images/ incubator/ooo/ooo-site/trunk/content/udk/python/images/customized_setup.png (with props) incubator/ooo/ooo-site/trunk/content/udk/python/images/mode_component.png (with props) incubator/ooo/ooo-site/trunk/content/udk/python/images/mode_ipc.png (with props) incubator/ooo/ooo-site/trunk/content/udk/python/images/modes.sxd (with props) incubator/ooo/ooo-site/trunk/content/udk/python/images/optional_components.png (with props) incubator/ooo/ooo-site/trunk/content/udk/python/libpyuno.so.gz (with props) incubator/ooo/ooo-site/trunk/content/udk/python/makefile.mk incubator/ooo/ooo-site/trunk/content/udk/python/oood/ incubator/ooo/ooo-site/trunk/content/udk/python/oood/index.html (with props) incubator/ooo/ooo-site/trunk/content/udk/python/oood/makefile.mk incubator/ooo/ooo-site/trunk/content/udk/python/oood/oood-0.1.0.zip (with props) incubator/ooo/ooo-site/trunk/content/udk/python/oood/oood-config.xml (with props) incubator/ooo/ooo-site/trunk/content/udk/python/oood/oood.py (with props) incubator/ooo/ooo-site/trunk/content/udk/python/python-bridge.html (with props) incubator/ooo/ooo-site/trunk/content/udk/python/pyuno-doc.zip (with props) incubator/ooo/ooo-site/trunk/content/udk/python/samples/ incubator/ooo/ooo-site/trunk/content/udk/python/samples/Addons.xcu incubator/ooo/ooo-site/trunk/content/udk/python/samples/biblioaccess.py (with props) incubator/ooo/ooo-site/trunk/content/udk/python/samples/hello_world_comp.py (with props) incubator/ooo/ooo-site/trunk/content/udk/python/samples/ooextract.py (with props) incubator/ooo/ooo-site/trunk/content/udk/python/samples/oomerge.py (with props) incubator/ooo/ooo-site/trunk/content/udk/python/samples/python-tokencounter-calc-addin.oxt (with props) incubator/ooo/ooo-site/trunk/content/udk/python/samples/swriter.py (with props) incubator/ooo/ooo-site/trunk/content/udk/python/samples/swritercomp.py (with props) incubator/ooo/ooo-site/trunk/content/udk/python/samples/swritercompclient.py (with props) incubator/ooo/ooo-site/trunk/content/udk/python/scriptingframework/ incubator/ooo/ooo-site/trunk/content/udk/python/scriptingframework/bilder.odp (with props) incubator/ooo/ooo-site/trunk/content/udk/python/scriptingframework/dynamicDialog.py (with props) incubator/ooo/ooo-site/trunk/content/udk/python/scriptingframework/index.html (with props) incubator/ooo/ooo-site/trunk/content/udk/python/scriptingframework/macro_run_dlg.PNG (with props) incubator/ooo/ooo-site/trunk/content/udk/python/scriptingframework/makefile.mk incubator/ooo/ooo-site/trunk/content/udk/python/scriptingframework/push_me_python4.odt (with props) incubator/ooo/ooo-site/trunk/content/udk/python/scriptingframework/pyhello2.uno.pkg (with props) Added: incubator/ooo/ooo-site/trunk/content/udk/python/download/pyuno-linux-x86-0.9.2.tar.gz URL: http://svn.apache.org/viewvc/incubator/ooo/ooo-site/trunk/content/udk/python/download/pyuno-linux-x86-0.9.2.tar.gz?rev=1206906&view=auto ============================================================================== Binary file - no diff available. Propchange: incubator/ooo/ooo-site/trunk/content/udk/python/download/pyuno-linux-x86-0.9.2.tar.gz ------------------------------------------------------------------------------ svn:mime-type = application/x-gzip Added: incubator/ooo/ooo-site/trunk/content/udk/python/download/pyuno-linux-x86-0.9.3.tar.gz URL: http://svn.apache.org/viewvc/incubator/ooo/ooo-site/trunk/content/udk/python/download/pyuno-linux-x86-0.9.3.tar.gz?rev=1206906&view=auto ============================================================================== Binary file - no diff available. Propchange: incubator/ooo/ooo-site/trunk/content/udk/python/download/pyuno-linux-x86-0.9.3.tar.gz ------------------------------------------------------------------------------ svn:mime-type = application/x-gzip Added: incubator/ooo/ooo-site/trunk/content/udk/python/download/pyuno-win32-0.9.2.zip URL: http://svn.apache.org/viewvc/incubator/ooo/ooo-site/trunk/content/udk/python/download/pyuno-win32-0.9.2.zip?rev=1206906&view=auto ============================================================================== Binary file - no diff available. Propchange: incubator/ooo/ooo-site/trunk/content/udk/python/download/pyuno-win32-0.9.2.zip ------------------------------------------------------------------------------ svn:executable = * Propchange: incubator/ooo/ooo-site/trunk/content/udk/python/download/pyuno-win32-0.9.2.zip ------------------------------------------------------------------------------ svn:mime-type = application/octet-stream Added: incubator/ooo/ooo-site/trunk/content/udk/python/download/pyuno-win32-0.9.3.zip URL: http://svn.apache.org/viewvc/incubator/ooo/ooo-site/trunk/content/udk/python/download/pyuno-win32-0.9.3.zip?rev=1206906&view=auto ============================================================================== Binary file - no diff available. Propchange: incubator/ooo/ooo-site/trunk/content/udk/python/download/pyuno-win32-0.9.3.zip ------------------------------------------------------------------------------ svn:executable = * Propchange: incubator/ooo/ooo-site/trunk/content/udk/python/download/pyuno-win32-0.9.3.zip ------------------------------------------------------------------------------ svn:mime-type = application/octet-stream Added: incubator/ooo/ooo-site/trunk/content/udk/python/images/customized_setup.png URL: http://svn.apache.org/viewvc/incubator/ooo/ooo-site/trunk/content/udk/python/images/customized_setup.png?rev=1206906&view=auto ============================================================================== Binary file - no diff available. Propchange: incubator/ooo/ooo-site/trunk/content/udk/python/images/customized_setup.png ------------------------------------------------------------------------------ svn:mime-type = image/png Added: incubator/ooo/ooo-site/trunk/content/udk/python/images/mode_component.png URL: http://svn.apache.org/viewvc/incubator/ooo/ooo-site/trunk/content/udk/python/images/mode_component.png?rev=1206906&view=auto ============================================================================== Binary file - no diff available. Propchange: incubator/ooo/ooo-site/trunk/content/udk/python/images/mode_component.png ------------------------------------------------------------------------------ svn:mime-type = image/png Added: incubator/ooo/ooo-site/trunk/content/udk/python/images/mode_ipc.png URL: http://svn.apache.org/viewvc/incubator/ooo/ooo-site/trunk/content/udk/python/images/mode_ipc.png?rev=1206906&view=auto ============================================================================== Binary file - no diff available. Propchange: incubator/ooo/ooo-site/trunk/content/udk/python/images/mode_ipc.png ------------------------------------------------------------------------------ svn:mime-type = image/png Added: incubator/ooo/ooo-site/trunk/content/udk/python/images/modes.sxd URL: http://svn.apache.org/viewvc/incubator/ooo/ooo-site/trunk/content/udk/python/images/modes.sxd?rev=1206906&view=auto ============================================================================== Binary file - no diff available. Propchange: incubator/ooo/ooo-site/trunk/content/udk/python/images/modes.sxd ------------------------------------------------------------------------------ svn:mime-type = application/octet-stream Added: incubator/ooo/ooo-site/trunk/content/udk/python/images/optional_components.png URL: http://svn.apache.org/viewvc/incubator/ooo/ooo-site/trunk/content/udk/python/images/optional_components.png?rev=1206906&view=auto ============================================================================== Binary file - no diff available. Propchange: incubator/ooo/ooo-site/trunk/content/udk/python/images/optional_components.png ------------------------------------------------------------------------------ svn:mime-type = image/png Added: incubator/ooo/ooo-site/trunk/content/udk/python/libpyuno.so.gz URL: http://svn.apache.org/viewvc/incubator/ooo/ooo-site/trunk/content/udk/python/libpyuno.so.gz?rev=1206906&view=auto ============================================================================== Binary file - no diff available. Propchange: incubator/ooo/ooo-site/trunk/content/udk/python/libpyuno.so.gz ------------------------------------------------------------------------------ svn:executable = * Propchange: incubator/ooo/ooo-site/trunk/content/udk/python/libpyuno.so.gz ------------------------------------------------------------------------------ svn:mime-type = application/x-gzip Added: incubator/ooo/ooo-site/trunk/content/udk/python/makefile.mk URL: http://svn.apache.org/viewvc/incubator/ooo/ooo-site/trunk/content/udk/python/makefile.mk?rev=1206906&view=auto ============================================================================== --- incubator/ooo/ooo-site/trunk/content/udk/python/makefile.mk (added) +++ incubator/ooo/ooo-site/trunk/content/udk/python/makefile.mk Sun Nov 27 23:07:13 2011 @@ -0,0 +1,51 @@ +PRJNAME=udkwww +PRJ=.. + +.INCLUDE : settings.mk +.INCLUDE : pyversion.mk + +ROOT=$(MISC)$/pyuno-doc +SCRIPTRT_VERSION=0.1.0 + +FILES=\ + $(ROOT)$/python-bridge.html \ + $(ROOT)$/images$/customized_setup.png \ + $(ROOT)$/images$/mode_component.png \ + $(ROOT)$/images$/mode_ipc.png \ + $(ROOT)$/images$/optional_components.png \ + $(ROOT)$/samples$/swriter.py \ + $(ROOT)$/samples$/swritercomp.py \ + $(ROOT)$/samples$/ooextract.py \ + $(ROOT)$/samples$/oomerge.py \ + $(ROOT)$/samples$/biblioaccess.py \ + $(ROOT)$/samples$/swritercompclient.py \ + $(ROOT)$/samples$/hello_world_pyuno.zip \ + $(ROOT)$/samples$/hello-framework-python.sxp \ + $(ROOT)$/scriptrt4python-$(SCRIPTRT_VERSION).zip + +$(MISC)$/pyuno-doc.zip : $(FILES) + -rm -f $@ + cd $(MISC) && zip -r pyuno-doc.zip pyuno-doc + +$(ROOT)$/samples$/hello_world_pyuno.zip : samples$/hello_world_comp.py samples$/Addons.xcu + -+$(MKDIRHIER) $(@:d) + -rm -f $@ + zip $@ $< + +$(ROOT)$/scriptrt4python-$(SCRIPTRT_VERSION).zip : \ + scriptingframework/runtime.py scriptingframework/Scripting.xcu + -+$(MKDIRHIER) $(@:d) + -rm -f $@ + zip $@ $< + +$(ROOT)$/samples$/hello-framework-python.sxp : scriptingframework/HelloFramework.py scriptingframework/parcel-descriptor.xml + -+$(MKDIRHIER) $(@:d) + -rm -f $@ + +cd scriptingframework && zip ..$/$@ HelloFramework.py parcel-descriptor.xml + +$(ROOT)$/% : % + -+$(MKDIRHIER) $(@:d) + -rm -f $@ + cat $? > $@ + + Added: incubator/ooo/ooo-site/trunk/content/udk/python/oood/index.html URL: http://svn.apache.org/viewvc/incubator/ooo/ooo-site/trunk/content/udk/python/oood/index.html?rev=1206906&view=auto ============================================================================== --- incubator/ooo/ooo-site/trunk/content/udk/python/oood/index.html (added) +++ incubator/ooo/ooo-site/trunk/content/udk/python/oood/index.html Sun Nov 27 23:07:13 2011 @@ -0,0 +1,333 @@ + + + +oood.py - a simple daemon for OpenOffice.org + + +

oood.py - A simple daemon for OpenOffice.org

+ +

oood.py is a daemon, which controls a pool of 'anonymous' office instances +(workers). +

+The workers can be used as backend for Java/python/C++ batch +processes for document conversion, mail merges, etc. You don't need to rewrite your current scripts, +a client connects to a daemon-controlled office just as if would connect to a normal office. Up to +now, I only checked the functionality for batch clients, for server clients (e.g. a tomcat or zope), +there may be some problems, you should simply try it. + +

The daemon ensures, that only one client at a time is connected to one OpenOffice instance +(because one OOo instance in general can't cope with more than one scripter). Workers +get restarted after a certain amount of uses or after office crashes. + +

A client can connect to a daemon as if it would connect to a normal 'non-daemoned' office, +so you don't need to adapt your scripts. + +

oood.py has been implemented in pure python, but it +uses some office components. This should make it easy to modify the daemon +to your needs if desired. + +

+Download: oood-0.1.0.zip (less than 10k): +

State

+The daemon comes in version 0.1.0. It is in a alpha state, it has +currently only be tested on Linux x86. It may run on other Unix +platforms, but it is definitely known not to run on windows. Simply try +out to check if it is useful for you (after carefully reading this manual). + +

+The daemon and this document is targeted at experienced OOo script developers. + +

Security

+The daemon and its usage is in general INSECURE. Everyone, who can connect +to the daemon can use the underlying office instances and thus +has full access to the machine (with the daemon's user rights) and +via socket communication to other machines accessible via sockets +from the worker machine. + +All worker instances run under the same (= the daemon's userid) meaning +that a menace user may spy other worker office instances. + +

+ +However, some simple limitations can be done. +

    +
  • Limiting the access to the daemon.
    + You can use the connection string to limit the access to a certain network interface. + E.g. using + + socket,host=localhost,port=2002;urp + + means, that the daemon (and the underlying office instances) can only be accessed + from the same machine, where the daemon is running on. + + One may easily extend the daemon source to limit access e.g. to certain hosts. + + There is no user administration. + +
  • User rights
    + Create a special user for running the office instances. Limit the + user's rights to the absolute minimum. +
+ +You should use this solution only in a trustworthy environments. + +

Installation

+ +

Office installation

+It is assumed, that you use an office from the 1.1.x series. +The office daemon works on an arbitrary number of office user installations, which +must have been created from the same network installation with a single system user. + +Ideally you create a new system user (e.g. oood) therefor, but if you just +want to try it out, you can use your normal system user. + +(The following description is more or less copied from a mail by J. Barfurth +in dev@api.openoffice.org). + +First do a new multi-user installation ( start + +$ setup -net + +) from the downloaded installation set. + +Afterwards, create multiple single user installations by starting +

+(use 01 instead of XX) +

+ +$ setup -d /home/oood/ooo1.1_srvXX + +

+from within the office/program directory. +After the setup run, edit ~/.sversionrc file and replace +"OpenOffice.org 1.1.0" with "OpenOffice.org 1.1.0_srvXX". . + +Repeat these steps with XX = 02, 03, ... . You need as many installations +as you expect concurrent users. You may also start with a low number +and add instances later on. + +Afterwards, your .sversionrc file should look like + +

+[Versions]
+OpenOffice.org 1.1 srv01=file:///home/oood/ooo1.1_srv01
+OpenOffice.org 1.1 srv02=file:///home/oood/ooo1.1_srv02
+OpenOffice.org 1.1 srv03=file:///home/oood/ooo1.1_srv03
+
+ +

Daemon installation

+Switch to the OpenOffice.org's program directory and +extract the oood-0.1.0.zip file. Open the oood-0.1.0/oood-config.xml file +in a text editor and add the paths of every worker instances with a + +<user-installation url="file:///home/oood/ooo1.1_srv01" /> + +tag. For a start, just add one or two instances to see how the daemon is working. +All other settings in oood-config.xml can be left untouched. The meaning +of the other settings are documented in the comments. + +

Daemon administration

+

Starting

+The daemon must be started with OpenOffice.org's python from within the OOo's program +directory. +

+ + +$ ./python oood-0.1.0/oood.py -c oood-0.1.0/oood-config.xml run + +

+You get the log on the stdout blocking the shell. Depending on the number of +workers you have configured, it may +take quite a while to start. When you get a + +

+Accepting on <your-connection-string> +

+ +the daemon is ready to serve requests. + +

Stopping

+ +From a different shell, start +

+$ ./python oood-0.1.0/bin/oood.py -c oood-0.1.0/config/oood-config.xml stop + +

+ +Signals the daemon to terminate all running workers and itself. + +The daemon can only be stopped this way after a successful startup. + +

Requesting status information

+

+$ ./python oood-0.1.0/oood.py -c oood-0.1.0/oood-config.xml status +

+Gives you a list of workers and their state. + +

Usage patterns

+You can now connect to the daemon with an arbitrary (Java, C++, python) +client program in exactly the same way as you connect to a normal +OpenOffice.org. + +

The daemon delegates your request to one of its worker offices. For the +time of usage, this worker office is exclusively used by your client program. +The end of usage is detected by the daemon through a breakdown +of the interprocess bridge (which occurs, when the last +reference is gone, the client explicitly disposes the remote bridge or +the client process terminates). + +

Performance

+All requests to the office are tunneled through the daemon process. This +means an additional load on the server machine and a performance overhead +for every request. This is typically neglectable when your call frequency +is low (say less than 10 Calls/s), but becomes a significant overhead +for higher call frequencies. + +

Logging

+

Log level

+

There is 3 log levels.

+ + + + + +
SERIOUSOnly startup information and errors get written into the log
INFOinformation about every connect and disconnect get logged (default)
DETAILLog level mostly sensible for debugging
+ +

Level INFO includes SERIOUS, DETAIL includes INFO and HIGH.

+ +

Log format

+Every line, that gets logged, has the following format + +
+current-time [loglevel] : Logtext
+
+ +The numbers in curly brackets (e.g. {2/5}) in the logtext signals +the free/total number of worker processes in the pool . + +

Startup log

+A typical startup log looks as follows ( on INFO loglevel) +
+
+Wed Nov 26 19:11:19 2003 [SERIOUS]: Started on pid 674
+Wed Nov 26 19:11:19 2003 [INFO   ]: Starting office workers ...
+Wed Nov 26 19:11:19 2003 [INFO   ]: Worker-0:<oood.OfficeProcess
+       file:///home/joergl/OpenOffice.org1.1.0_instance-0;
+              pid=692;connectStr=pipe,name=oood-instance-0,usage=0> started
+Wed Nov 26 19:11:59 2003 [INFO   ]: {2/2} WorkerAll instances started
+Wed Nov 26 19:11:59 2003 [SERIOUS]: Accepting on socket,host=localhost,port=2002;urp
+
+
+First line gives the pid of the daemon process (in case +you want to terminate the process during startup). Then follows for +every worker process, that gets started a line showing the used +home directory and connection string. + +

Working log

+Below you can see a typical log of a single connect attempt: +
+Wed Nov 26 19:24:02 2003 [INFO  ]: {1/2} -> Worker-0(1 uses) serves localhost:32770
+Wed Nov 26 19:24:13 2003 [INFO  ]: localhost:32770 disconnects from Worker-0(1 uses) (used for  10.7s) 
+Wed Nov 26 19:24:13 2003 [INFO  ]: {2/2} <- Worker-0(1 uses) reenters pool
+
+
+ +First line states that out of the pool Worker-0 1is used to serve the incoming +request from localhost:32770. The {1/2} indicates, that there one free worker +left is in the pool. + +Second line states, that the interprocess bridge between the daemon +and the requesting process has broken down. Additionally, +the number of uses and the duration of the last use in seconds is shown. + +In case, the number of uses is less than the max-usage-count-per-instance, +the process is simply added to the pool of available offices again (as it +documented by the third line. The {2/2} states, that there are now exactly +two workers in the pool again. + +In case the max-usage-count-per-instance is exceeded, this worker instance is automatically +terminated and a fresh instance is restarted. This shall tide up memory leaks. + +When there is no free worker left anymore and a client request comes in, +the request is rejected and a line like the following gets logged: +
+Sat May 22 22:28:46 2004 [SERIOUS]: {0/2} localhost:32776 rejected
+
+ +The client script simply gets a empty reference instead of the requested object. +

+ +Note: Preferably it would receive a RuntimeException with an appropriate +message, but a bug somewhere around pyuno, the scripting components +and the remote bridge currently prevents this (the daemons crashes +quite fast). + + +

Termination log

+Once daemon termination has been initiated, the log should look like the following: +
+Sat May 22 22:33:00 2004 [SERIOUS]: Accepting on socket,host=localhost,port=2002;urp stopped, waiting for shutdownthread
+Sat May 22 22:33:00 2004 [INFO   ]: Admin thread terminating
+Sat May 22 22:33:00 2004 [INFO   ]: terminating Worker-0(1 uses)
+Sat May 22 22:33:00 2004 [INFO   ]: Worker-0(1 uses) terminated
+Sat May 22 22:33:00 2004 [INFO   ]: terminating Worker-1(1 uses)
+Sat May 22 22:33:00 2004 [INFO   ]: Worker-1(1 uses) terminated
+Sat May 22 22:33:00 2004 [SERIOUS]: Terminating normally
+
+

Robustness

+Robustness and stability is certainly a key feature of a daemon. The following +situations are currently handled: + +
    +
  • Running out of workers
    +In case all worker instances are busy and the pool is empty, every new +connection attempt is rejected, the client receives an empty reference +instead of the servicemanager or componentcontext object. + +

    + +Note: Preferably it would receive a RuntimeException with an appropriate +message, but a bug somewhere around pyuno, the scripting components +and the remote bridge currently prevents this (the daemons crashes +quite fast). + + +

    +The connection attempt gets logged appropriately. +

  • A worker office crashes or deadlocks
    +Before a worker reenters the pool, it is checked, whether +it is still responsive and it checks whether a deadlock +with the solarmutex blocks the whole office. In case +such a situation occurred, the worker is killed and a fresh +instance is started and added again to the pool. + +

    +The check is currently quite rudimentary, it may +be improved in future. +

  • + +
  • Worker processes are restarted after a certain amount of client uses. This ensures, +that an ill office instance will die sooner or later. +
  • + +
  • Note: In case the daemon itself crashes (I am currently not aware of such a situation), + the worker instances don't + terminate, an admin needs to kill the instances by hand and restart the daemon. +
  • + +
+ +

License

+As you are used to when using OOo, this program is LGPL. + +

Feedback

+Please give feedback through dev@api.openoffice.org mailing list. + + +

Author

+The daemon has been developed by Joerg Budischewski. +I'd very much welcome patches for an improved daemon and would even very much welcome someone +taking over maintenance for the daemon. + + + Propchange: incubator/ooo/ooo-site/trunk/content/udk/python/oood/index.html ------------------------------------------------------------------------------ svn:eol-style = native Added: incubator/ooo/ooo-site/trunk/content/udk/python/oood/makefile.mk URL: http://svn.apache.org/viewvc/incubator/ooo/ooo-site/trunk/content/udk/python/oood/makefile.mk?rev=1206906&view=auto ============================================================================== --- incubator/ooo/ooo-site/trunk/content/udk/python/oood/makefile.mk (added) +++ incubator/ooo/ooo-site/trunk/content/udk/python/oood/makefile.mk Sun Nov 27 23:07:13 2011 @@ -0,0 +1,26 @@ +PRJNAME=udkwww +PRJ=../.. + +.INCLUDE : settings.mk +.INCLUDE : pyversion.mk + +VERSION=0.1.0 + +ROOT=$(MISC)$/oood-$(VERSION) + +FILES=\ + $(ROOT)/oood.py \ + $(ROOT)/index.html \ + $(ROOT)/oood-config.xml + + +$(ROOT).zip : $(FILES) + -rm -f $@ + cd $(MISC) && zip -r oood-$(VERSION).zip oood-$(VERSION) + md5sum $@ + + +$(ROOT)/% : % + -+$(MKDIRHIER) $(@:d) + rm -f $@ + cat $< > $@ Added: incubator/ooo/ooo-site/trunk/content/udk/python/oood/oood-0.1.0.zip URL: http://svn.apache.org/viewvc/incubator/ooo/ooo-site/trunk/content/udk/python/oood/oood-0.1.0.zip?rev=1206906&view=auto ============================================================================== Binary file - no diff available. Propchange: incubator/ooo/ooo-site/trunk/content/udk/python/oood/oood-0.1.0.zip ------------------------------------------------------------------------------ svn:mime-type = application/octet-stream Added: incubator/ooo/ooo-site/trunk/content/udk/python/oood/oood-config.xml URL: http://svn.apache.org/viewvc/incubator/ooo/ooo-site/trunk/content/udk/python/oood/oood-config.xml?rev=1206906&view=auto ============================================================================== --- incubator/ooo/ooo-site/trunk/content/udk/python/oood/oood-config.xml (added) +++ incubator/ooo/ooo-site/trunk/content/udk/python/oood/oood-config.xml Sun Nov 27 23:07:13 2011 @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + Propchange: incubator/ooo/ooo-site/trunk/content/udk/python/oood/oood-config.xml ------------------------------------------------------------------------------ svn:eol-style = native Added: incubator/ooo/ooo-site/trunk/content/udk/python/oood/oood.py URL: http://svn.apache.org/viewvc/incubator/ooo/ooo-site/trunk/content/udk/python/oood/oood.py?rev=1206906&view=auto ============================================================================== --- incubator/ooo/ooo-site/trunk/content/udk/python/oood/oood.py (added) +++ incubator/ooo/ooo-site/trunk/content/udk/python/oood/oood.py Sun Nov 27 23:07:13 2011 @@ -0,0 +1,678 @@ +#************************************************************************* +# +# $RCSfile: oood.py,v $ +# +# $Revision: 1.1 $ +# +# last change: $Author: jbu $ $Date: 2004/10/03 17:41:40 $ +# +# The Contents of this file are made available subject to the terms of +# either of the following licenses +# +# - GNU Lesser General Public License Version 2.1 +# - Sun Industry Standards Source License Version 1.1 +# +# Sun Microsystems Inc., October, 2000 +# +# GNU Lesser General Public License Version 2.1 +# ============================================= +# Copyright 2000 by Sun Microsystems, Inc. +# 901 San Antonio Road, Palo Alto, CA 94303, USA +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License version 2.1, as published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +# +# +# Sun Industry Standards Source License Version 1.1 +# ================================================= +# The contents of this file are subject to the Sun Industry Standards +# Source License Version 1.1 (the "License"); You may not use this file +# except in compliance with the License. You may obtain a copy of the +# License at http://www.openoffice.org/license.html. +# +# Software provided under this License is provided on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, +# WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS, +# MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING. +# See the License for the specific provisions governing your rights and +# obligations concerning the Software. +# +# The Initial Developer of the Original Code is: Joerg Budischewski +# +# Copyright: 2000 by Sun Microsystems, Inc. +# +# All Rights Reserved. +# +# Contributor(s): Joerg Budischewski +# +#************************************************************************* +import uno +import unohelper +import os +import time +import sys +import signal +import threading +import random + +from com.sun.star.bridge import XInstanceProvider +from com.sun.star.connection import NoConnectException, ConnectionSetupException +from com.sun.star.io import XStreamListener +from com.sun.star.lang import IllegalArgumentException +from com.sun.star.uno import RuntimeException +from com.sun.star.xml.sax import XDocumentHandler, InputSource +from com.sun.star.io import XInputStream +from com.sun.star.container import XNameAccess +from com.sun.star.beans import NamedValue + +SERIOUS = 0 +INFO = 1 +DETAIL = 2 +LEVEL2STRING = { SERIOUS : "SERIOUS", INFO : "INFO " , DETAIL : "DETAIL " } +COMMANDS = ( "run", "stop" , "status" ) +cmd = None +configfile = None + +def usage(): + print "usage: oood.py -c config-file run|stop|status" + print " daemon for openoffice" + + +i = 1 +while i < len(sys.argv): + if sys.argv[i] == "-c": + i = i + 1 + configfile = sys.argv[i] + elif sys.argv[i] in COMMANDS: + cmd = sys.argv[i] + else: + usage() + os._exit(1) + i = i + 1 + +if cmd == None or configfile == None: + usage() + os._exit(1) + + +class FileInputStream( XInputStream, unohelper.Base ): + def __init__( self, path ): + self.f = file( path ) + + def closeInput( self): + self.f.close() + + def skipBytes( self, nByteCount ): + self.f.read( nByteCount ) + + def readBytes( self, retSeq, nByteCount ): + s = self.f.read( nByteCount ) + return len( s ) , uno.ByteSequence( s ) + + def readSomeBytes( self, retSeq , nByteCount ): + #as we never block ! + return self.readBytes( retSeq, nByteCount ) + + def available( self ): + return 0 + +class Config: + def __init__( self ): + self.acceptor = "" + self.userInstallation = () + self.toleratedStartupTimePerInstance = 180 + self.maxUsageCountPerInstance = 10 + self.randomUsageCountPerInstance = 3 + self.loglevel = INFO + + def __str__(self): + return "acceptor="+self.acceptor+", userInstallation="+str(self.userInstallation) + \ + ", toleratedStartupTimePerInstance=" + str( \ + self.toleratedStartupTimePerInstance ) + ", maxUsageCountPerInstance="+ \ + str( self.maxUsageCountPerInstance ) + ", randomUsageCountPerInstance=" + \ + str( self.randomUsageCountPerInstance ) + ",loglevel= "+ str(self.loglevel) + +class ConfigHandler( XDocumentHandler, unohelper.Base ): + def __init__( self ): + pass + + def startDocument( self ): + self.config = Config() + + def endDocument( self ): + pass + + def startElement( self , name, attlist): + if name == "acceptor": + self.config.acceptor = attlist.getValueByIndex(0 ) + elif name == "admin-acceptor": + self.config.adminAcceptor = attlist.getValueByIndex(0 ) + elif name == "user-installation": + self.config.userInstallation = self.config.userInstallation + ( + attlist.getValueByName( "url" ), ) + elif name == "tolerated-startuptime-per-instance": + self.config.toleratedStartupTimePerInstance = int( + attlist.getValueByName( "value" ) ) + elif name == "usage-count-per-instance": + self.config.maxUsageCountPerInstance = int( + attlist.getValueByName( "max" ) ) + self.config.randomUsageCountPerInstance = int( + attlist.getValueByName( "random" ) ) + elif name == "logging": + l = attlist.getValueByName( "level" ) + if l == "info": + self.config.loglevel = INFO + elif l == "serious": + self.config.loglevel = SERIOUS + elif l == "detail": + self.config.loglevel = DETAIL + else: + raise RuntimeException( "Unknown loglevel " + l , None ) + + def endElement( self, name ): + pass + + def characters ( self, chars ): + pass + + def ignoreableWhitespace( self, chars ): + pass + + def setDocumentLocator( self, locator ): + pass + +def readConfiguration( path, parser ): + h = ConfigHandler() + parser.setDocumentHandler( h ) + parser.parseStream( + InputSource( FileInputStream( path ) , "", path, path ) ) + return h.config + +def namedValueTupleToMap( t ): + m = {} + for i in t: + m[ i.Name ] = i.Value + return m + +ctx = uno.getComponentContext() +config = readConfiguration( + configfile,ctx.ServiceManager.createInstance("com.sun.star.xml.sax.Parser")) + +if cmd == "stop": + uno.getComponentContext().ServiceManager.createInstance( + "com.sun.star.bridge.UnoUrlResolver").resolve( + "uno:"+config.adminAcceptor+";oood.Shutdown" ) + os._exit(0) +elif cmd == "status": + status = uno.getComponentContext().ServiceManager.createInstance( + "com.sun.star.bridge.UnoUrlResolver").resolve( + "uno:"+config.adminAcceptor+";oood.Status" ) + + if status == None: + print "Couldn't resolve status object" + print "Instances in daemon (free/total): " +str(status.getByName( "available" )) + \ + "/" + str( status.getByName( "poolsize" ) ) + + workers = status.getByName( "workers" ) + print "Worker\tpid\tin use\tusages\tduration\tuser-directory" + for i in workers: + out = "" + m = namedValueTupleToMap( i ) + inuse = " " + duration = " \t" + if m["usage-time"] > 0: + inuse = "x" + duration = str( round(m["usage-time" ],2) ) + "s \t" + + print str( m["index"]) + "\t" + \ + str( m["pid"] ) + " \t" + \ + inuse +"\t" + \ + str( m["usage"] ) + "\t" + \ + duration +\ + str( m["user-dir" ] ) + os._exit(0) + +NULL_DEVICE = "/dev/null" +processPool = None + + +class PoolAdderThread( threading.Thread ): + def __init__( self, process ): + threading.Thread.__init__( self ) + self.process = process + + def run( self ): + try: + if not self.process.restartWhenNecessary(): + logger.log( SERIOUS, "FATAL: could not restart worker " + + str(self.process) + ", terminating now !" ) + os._exit(1) + processPool.append( self.process ) + logger.log( INFO, processPool.getStateString()+" <- "+str(self.process)+ + " reenters pool" ) + except Exception,e: + logger.log( SERIOUS, str(e) ) + + +class Status( unohelper.Base, XNameAccess ): + def __init__( self, processList ): + self.map = {} + self.map[ "poolsize" ] = len( processList ) + available = 0 + workers = [] + for i in processList: + v = None + if i.timestamp == None: + v = NamedValue( "usage-time" , 0 ) + available = available + 1 + else: + v = NamedValue( "usage-time", time.time() - i.timestamp ) + + t = NamedValue( "pid", i.pid ), \ + NamedValue( "usage", i.usage ), \ + v, \ + NamedValue( "user-dir" , i.userid ), \ + NamedValue( "index", i.index ) + workers.append( t ) + self.map[ "workers" ] = tuple( workers ) + self.map[ "available" ] = available + + def getByName( self, name ): + if self.map.has_key( name ): + return self.map[ name ] + raise NoSuchElementException( "unknown element " + name, self ) + + def getElementNames( self ): + return tuple( self.map.keys() ) + + def hasByName( self , name ): + return self.map.has_key( name ) + + def getElementType( self ): + return Type() + + def hasElements( self ): + return True + +class ProcessPool: + def __init__( self ): + self.lst = [] + self.mutex = threading.Lock() + + def append( self , item ): + self.mutex.acquire() + self.lst.append( item ) + self.mutex.release() + + def initializationFinished( self ): + self.all = tuple( self.lst ) + + def size( self ): + return len( self.lst ) + + def terminate( self ): + for i in self.all: + i.terminate() + + def pop( self ): + ret = None + while ret == None: + self.mutex.acquire() + if len(self.lst) == 0: + self.mutex.release() + break + ret = self.lst.pop(0) + self.mutex.release() + if not ret.isResponsive(): + # process has died inbetween + PoolAdderThread( ret ).start() + ret = None + return ret + + def waitTillReady( self ): + for i in self.lst: + if not i.waitTillReady( + self.size() * config.toleratedStartupTimePerInstance ): + os._exit(1) + def getStateString( self): + global config + return "{" + str(self.size()) + "/" + str(len(config.userInstallation)) + "}" + +class Logger: + def __init__( self , out, level ): + self.out = out + self.level = level + + def log( self, level , text ): + if level <= self.level: + self.out.write( + time.asctime() + " ["+ + LEVEL2STRING[level] +"]: " + text + "\n") + +processPool = ProcessPool() +random.seed() +logger = Logger( sys.stdout, config.loglevel ) +shutdownThread = None + +acceptor = ctx.ServiceManager.createInstance( + "com.sun.star.connection.Acceptor" ) +bridgefactory = ctx.ServiceManager.createInstance( + "com.sun.star.bridge.BridgeFactory" ) +connector = ctx.ServiceManager.createInstance( + "com.sun.star.connection.Connector" ) + +def getConnectString( index ): + return "pipe,name=oood-instance-" + str(index) + +class AdminInstanceProvider( unohelper.Base, XInstanceProvider ): + def getInstance( self, name ): + object = None + if name == "oood.Shutdown": + global shutdownThread + shutdownThread = threading.Timer( 1.0, shutdown , (0,processPool) ) + shutdownThread.start() + elif name == "oood.Status": + object = Status( processPool.all ) + else: + logger.log( DETAIL, "AdminInstanceProvider: Unknown object " +name ) + return object + +class AdminAcceptorThread( threading.Thread ): + def __init__( self , ctx, acceptString ): + threading.Thread.__init__(self) + self.ctx = ctx + self.acceptString = acceptString + self.acceptor = self.ctx.ServiceManager.createInstance( + "com.sun.star.connection.Acceptor") + + def run( self ): + logger.log( INFO, "Admin thread started" ) + while True: + c = self.acceptor.accept( self.acceptString ) + if c == None: + break + logger.log( DETAIL, "Accepted admin connection from "+ + extractContactInfo(c.getDescription())) + bridgefactory.createBridge( "", "urp", c, AdminInstanceProvider() ) + + logger.log( INFO, "Admin thread terminating" ) + + def cancel( self ): + self.acceptor.stopAccepting( ) + +class TerminateThread( threading.Thread ): + def __init__( self, ctx ): + threading.Thread.__init__( self ) + self.ctx = ctx + + def run( self ): + try: + self.ctx.ServiceManager.createInstance( "com.sun.star.frame.Desktop").terminate() + except Exception: + pass + +class ResponsivenessChecker( threading.Thread ): + def __init__( self, process ): + threading.Thread.__init__(self) + self.process = process + self.responsive = False + + def isResponsive( self ): + self.join( 4 ) + return self.responsive + + def run( self ): + try: + # still alive ? + smgr = self.process.ctx.ServiceManager + desktop = smgr.createInstance( "com.sun.star.frame.Desktop" ) + + # check for typical solar-mutex deadlock + desktop.getCurrentComponent() + + # more checks may be added + self.responsive = True + + except Exception,e: + logger.log( SERIOUS, "responsiveness-check for " + str( self.process) + + " failed: " + str(e) ) + +def calculateMaxUsageCount(): + return config.maxUsageCountPerInstance + \ + ( 1. - 2*random.random()) * config.randomUsageCountPerInstance + +def shutdown( returncode , pool ): + acceptor.stopAccepting() + pool.terminate() + +class OfficeProcess: + def __init__( self , userid, index): + self.userid = userid + self.index = index + self.pid = None + self.usage = 0 + self.timestamp = None + self.bridge = None + self.ctx = None + + def start(self): + self.pid = os.spawnlp( + os.P_NOWAIT, + "soffice", + "" , # what is this for a string ? + "-env:UserInstallation="+self.userid , + "-headless", + "-norestore", + "-invisible", + "-accept="+getConnectString(self.index)+ ";urp;" ) + + def kill( self ): + if self.pid: + os.kill( self.pid, signal.SIGKILL ) + logger.log( INFO, str( self ) + " killed" ) + + def isAlive( self ): + return os.system( "ps -p " + str( self.pid ) + " >" + NULL_DEVICE ) == 0 + + def terminate( self ): + if self.ctx: + t = TerminateThread( self.ctx ) + logger.log( INFO, "terminating " +str( self ) ) + t.start() + t.join( 4 ) + if t.isAlive(): + logger.log( + SERIOUS, repr( self ) + " did not react on terminate, killing instance" ) + self.kill() + else: + logger.log( INFO, str( self ) + " terminated" ) + self.ctx = None + + def terminateAndRestart( self ): + self.terminate() + time.sleep( 4 ) + self.start() + if not self.waitTillReady( config.toleratedStartupTimePerInstance ): + logger.log( SERIOUS, "could not restart instance "+str(self)+", terminating" ) + return False + self.usage = 0 + return True + + def tryConnect( self ): + try: + con = connector.connect( getConnectString( self.index ) ) + self.bridge = bridgefactory.createBridge( "", "urp" , con, None ) + self.ctx = self.bridge.getInstance( "StarOffice.ComponentContext" ) + return self.ctx != None + except NoConnectException,e: + logger.log( DETAIL, str(self)+ " not yet responsive" ) + except Exception,e: + logger.log( SERIOUS , "couldn't connect to instance ("+str(e)+")" ) + return False + + def waitTillReady( self , timeout ): + start = time.time() + while not self.tryConnect( ) and time.time()-start < timeout: + time.sleep( 4 ) + + if time.time() - start > timeout: + return False + return True + + def __str__(self): + return "Worker-" + str(self.index) + "("+str(self.usage)+" uses)" + + def __repr__(self): + return "" % \ + (self.userid,self.pid,getConnectString(self.index),self.usage) + + def startUsage( self ): + self.usage = self.usage + 1 + self.timestamp = time.time() + + def getUsageDuration( self ): + return time.time() - self.timestamp + + def endUsage( self ): + self.timestamp = None + + def isResponsive( self ): + t = ResponsivenessChecker( self ) + t.start() + return t.isResponsive() + + def restartWhenNecessary( self ): + if not self.isResponsive(): + logger.log( INFO, "process " + str(self) +\ + " not responsive anymore, restarting" ) + self.usage = 0 + return self.terminateAndRestart() + + if self.usage >= calculateMaxUsageCount(): + logger.log( INFO, "max usage count for instance " + str(self) +\ + " reached, restarting" ) + return self.terminateAndRestart() + return True + + + +class ConnectionListener( unohelper.Base, XStreamListener ): + def __init__( self , officeProcess, conDesc ): + self.officeProcess = officeProcess + self.conDesc = conDesc + + def clear( self ): + if self.officeProcess != None: + logger.log( INFO, self.conDesc + " disconnects from " + + str( self.officeProcess ) + " (used for "+ + str(round(self.officeProcess.getUsageDuration(),1)) +"s) " ) + self.officeProcess.endUsage( ) + PoolAdderThread( self.officeProcess ).start() + self.officeProcess = None + + def started( self ): + pass + + def closed( self ): + self.clear() + def terminated( self ): + self.clear() + def error( self , exception ): + self.clear() + + +class OfficeInstanceProvider( unohelper.Base, XInstanceProvider ): + def __init__( self, office ): + self.office = office + + def getInstance( self, name ): + logger.log( DETAIL, "resolving name " +name ) + object = self.office.bridge.getInstance( name ) + return object + +class EmptyPoolInstanceProvider( unohelper.Base, XInstanceProvider ): + def getInstance( self, name ): + return None +# raise RuntimeException( "No office instance available, try later" , None ) + +def extractContactInfo( namevalue ): + lst = namevalue.split( "," ) + host = "" + port = "" + for i in lst: + if i.startswith( "peerHost" ): + host = i.split("=")[1] + elif i.startswith( "peerPort" ): + port = i.split("=")[1] + return host + ":" + port + + +logger.log( SERIOUS, "Started on pid " + str( os.getpid() ) ) + + +index = 0 +logger.log( INFO, "Starting office workers ..." ) + +for i in config.userInstallation: + office = OfficeProcess( i , index ) + office.start() + logger.log( INFO, "Worker-" +str(index) + ":" + repr(office) +" started") + processPool.append( office ) + index = index + 1 + +processPool.waitTillReady() +processPool.initializationFinished() + +adminThread = AdminAcceptorThread( ctx, config.adminAcceptor ) +adminThread.start() + +logger.log( INFO , processPool.getStateString()+ " WorkerAll instances started" ) +logger.log( SERIOUS, "Accepting on " + config.acceptor ) + +while True: + con = acceptor.accept( config.acceptor ) + if con == None: + break + + conDesc = extractContactInfo(con.getDescription()) + logger.log( INFO , "Incoming request for a worker from " + conDesc ) + process = processPool.pop() + if process == None: + logger.log( SERIOUS, processPool.getStateString()+" " + conDesc + + " rejected, all workers are busy" ) + bridgefactory.createBridge( + "", "urp", con , EmptyPoolInstanceProvider( ) ) + else: + process.startUsage() + logger.log( INFO, processPool.getStateString()+" -> " + + str(process) +" serves "+conDesc) + + con.addStreamListener( ConnectionListener(process,conDesc) ) + bridgefactory.createBridge( + "", "urp", con , OfficeInstanceProvider( process ) ) + +logger.log( SERIOUS, "Accepting on " + config.acceptor + + " stopped, waiting for shutdownthread") + +adminThread.cancel() + +if shutdownThread != None: + shutdownThread.join() + +if adminThread != None: + adminThread.join() + +logger.log( SERIOUS, "Terminating normally") + + Propchange: incubator/ooo/ooo-site/trunk/content/udk/python/oood/oood.py ------------------------------------------------------------------------------ svn:eol-style = native