background image

rozwiązania

Demon konwersji

36

sierpień 2007

rozwiązania

Demon konwersji

37

www.lpmagazine.org

lin

ux

@

so

ftw

ar

e.

co

m

.p

l

P

rogram powstał pierwotnie na potrzeby systemu 
zarządzania  dokumentami  ERP5-DMS,  ale  je-
go możliwe zastosowania są znacznie szersze. 
Oood  jest  rozpowszechniany  na  licencji  GPL, 

a jego kod źródłowy jest dostępny w publicznym repozy-
torium systemu ERP5.

Architektura

Oood jest w zasadzie serwerem XMLRPC napisanym w ję-
zyku  Python  na  bazie  standardowego  modułu  Simple-
XMLRPCServer. Aplikacje klienckie napisane w Pythonie 
mogą  skorzystać  z  dostarczanej  przez  oood  biblioteki 
oood_common i standardowego modułu xmlrpclib. Rzecz ja-
sna – korzystać z usług serwera mogą także aplikacje napi-
sane w dowolnym innym języku. Serwer oood uruchamia 
jedną instancję OpenOffice'a, z którą następnie komunikuje 
się poprzez gniazdo (socket). Instancja ta korzysta ze swoje-
go katalogu domowego, ma więc swoją własną konfigura-
cję. Instancja jest kontrolowana przez serwer, który w razie 
potrzeby może ją zrestartować.

Serwer działa wielowątkowo, jest więc w stanie przy-

jąć  jednocześnie  wiele  żądań,  skolejkować  je  i  obsłużyć 

jedno po drugim. W przypadku wystąpienia problemów 
z otwarciem nadesłanego pliku lub ogólnych problemów 
z funkcjonowaniem serwera informuje on klientów o wy-
stąpieniu takiej sytuacji i podejmuje działania naprawcze, 
nie przerywając przy tym obsługi nadchodzących żądań.

Do komunikacji z klientami oood wykorzystuje spe-

cjalny protokół, nieco podobny do protokołu HTTP – każ-
da odpowiedź wysłana klientowi składa się z kodu, komu-
nikatu i danych żądanych przez klienta.

Taka architektura zapewnia stabilność systemu – te-

sty wykazały, że oood uruchomiony na zwykłym lapto-
pie jest w stanie obsłużyć 100 żądań konwersji pliku wy-
słanych jednocześnie – wszystkie żądania zostały prawi-
dłowo obsłużone. W ramach innego testu oood pracując 
bez przerwy przez trzy i pół dnia, skonwertował 100 000 
plików.

Instalacja i uruchomienie

Program można pobrać z publicznego repozytorium svn 
(pakiety  RPM  są  w  przygotowaniu).  Do  poprawnego 
działania  wymaga  Pythona  2.4  i  OpenOffice'a  2.0  bądź 
2.1  lub  Pythona  2.5  i  OpenOffice'a  2.2.  Opcje  konfigura-

OpenOffice 

– narzędzie do konwersji

Główny zamysł programu oood (skrót od OpenOffice.org Daemon) to udostępnienie programom klienckim 
olbrzymich możliwości OpenOffice'a w zakresie konwersji i edycji plików w różnych formatach poprzez prosty 
w użyciu i stabilny interfejs. Oood potrafi dokonywać konwersji dokumentów pomiędzy wszystkimi formatami 
obsługiwanymi przez OpenOffice'a – najczęściej potrzebna jest konwersja między formatami MSOffice a ODF, 
ale oood może także generować pdf-y, zamienić dokument tekstowy na docbook, arkusz kalkulacyjny 
na csv, a prezentację na zestaw stron www albo animację we Flashu. Jest w stanie także odczytać zapisane 
w dokumencie metadane, a także te metadane w dokumencie zmienić.

Bartłomiej Górny

background image

rozwiązania

Demon konwersji

36

sierpień 2007

rozwiązania

Demon konwersji

37

www.lpmagazine.org

cyjne  znajdują  się  w  pliku 

oood.conf

.  Kon-

figuracja  programu  jest  dość  prosta,  wystar-
czy  ustawić  ścieżkę  do  programu  i  do  miej-
sca,  gdzie  znajdują  się  pliki  wykonywalne 
OpenOffice'a,  port,  na  którym  ma  nasłuchi-
wać  oood  i  nazwę  lokalnej  drukarki.  Oczy-
wiście  użytkownik,  który  będzie  uruchamiał 
oood,  musi  mieć  prawo  do  zapisu  do  po-
danych  ścieżek.  Przy  starcie  oood  Open-
Office zostanie uruchomiony w tle, natomiast 
za  pierwszym  razem  należy  uruchomić  in-
stancję OpenOffice „na wierzchu” poleceniem: 

./start.py –top.

Następnie  przejść  przez  procedurę  reje-

stracji,  a  potem  w  uruchomionym  już  Open-
Office'ie ustawić ścieżkę do miejsca, gdzie za-
instalowany jest Java Runtime Environment. To 
ostatnie  znakomicie  przyspiesza  uruchamia-
nie OpenOffice'a, można więc śmiało zmniej-
szyć zmienną instance_load_time do 10 sekund, 
oood będzie działał znacznie prędzej.

Samego  demona  uruchamia  się  polece-

niem: 

./runserw.py –start.

Kiedy już zacznie działać, można podglądać, 

co dzieje się w środku poleceniami: 

./start.

py –status 

./start.py –threads.

Sposób użycia

Oood  nie  ma  interfejsu  użytkownika  –  jest 
przeznaczony wyłącznie do użytku przez ap-
likacje  klienckie.  Mogą  one  korzystać  z  pu-
blicznych  metod  serwera.  Jako  argument
funkcji  klient  powinien  przekazać  słownik
zawierający  odpowiednie  parametry  oraz
dane zakodowane przez base64. Rezultatem
wywołania  zdalnej  metody  jest  lista  zawie-
rająca trzy elementy:

•  kod (status)
•  słownik  z  danymi  zwróconymi  przez 

serwer

•  komunikat odpowiadający statusowi.

Listing 1. 

Python

sp=xmlrpclib.ServerProxy('http:
//localhost:8008')

data

 

=

 

open

(

'doc/test.doc'

)

.

read

()

   

encdata

 

=

 

base64

.

encodestring

(

data

)

response

 

=

 

sp

.

generate

(

{

'data'

:

encdata

'extension'

:

 

'pdf'

}

)

if

 

response

[

0

]

 

==

 

200

:

decdata

 

=

 

base64

.

decodestring

(

respo

nse

[

1

][

'data'

])

open

(

'test.pdf'

'w'

)

.

write

(

decdata

)

else

:

print

 

'nie udalo sie: '

response

[

2

]

Listing 2. 

Java

import java.util.*; import org.apache.xmlrpc.client.*;
import org.apache.ws.commons.util.*; import java.net.URL;

byte

[]

 

inData

 

=

 // zawartość pliku wejściowego

String

 

encData

 

=

 

Base64

.

encode

(

inData

)

;

XmlRpcClientConfigImpl

 

config

 

=

 

new

 

XmlRpcClientConfigImpl

()

;

config

.

setServerURL

(

new

 

URL

(

server_url

))

;

            

XmlRpcClient

 

client

 

=

 

new

 

XmlRpcClient

()

;

client

.

setConfig

(

config

)

;

HashMap

 

params

 

=

 

new

 

HashMap

()

;

 

params

.

put

(

"extension"

,

"pdf"

)

;

params

.

put

(

"data"

,

encData

)

;

 

Vector

 

paramVector

 

=

 

new

 

Vector

(

1

)

;

paramVector

.

add

(

params

)

;

Object

[]

 

result

 

=

 

(

Object

[])

client

.

execute

(

"generate"

paramVector

)

;

int

 

resultCode

 

=

 

((

Integer

)

result

[

0

])

.

intValue

()

;

if

 

(

resultCode

 

==

 

200

){

  

encData

 

=

 

(

String

)((

Map

)

result

[

1

])

.

get

(

"data"

)

;

  

byte

[]

 

outData

 

=

 

Base64

.

decode

(

encData

)

;

 

 // outData zawiera przekonwertowany plik

}

else

{

  

System

.

out

.

println

(

"nie udalo sie "

+

(

String

)

result

[

2

])

;

}

Listing 3. 

PHP

include

 

'xmlrpc.inc'

;

 // https://sourceforge.net/projects/phpxmlrpc/

function

 getDataItem

(

$response

$item

){

    

return

 

$response

[

'data'

]

-

>

structMem

(

$item

)

-

>

scalarval

()

;

}

function

 analyzeResponse

(

$res

){

    

$val

 = 

$res

-

>

value

()

;

    

$list

 = 

$val

-

>

scalarval

()

;

    

$status_code

 = 

$list

[

0

]

-

>

scalarval

()

;

    

$data

 = 

$list

[

1

]

;

    

$msg

 = 

$list

[

2

]

-

>

scalarval

()

;

    

return

 

array

(

'code'

=

>

$status_code

'data'

=

>

$data

'message'

=

>

$msg

)

;

}

function

 makeRequest

(

$method

$params

){

    

$newparams

 = 

array

()

;

    

foreach

(

$params

 as 

$k

=

>

$v

){

        

if

(

$k

 == 

'data'

){

   

$v

 = base64_encode

(

$v

)

;

        

}

        

$newparams

[

$k

]

 = 

new

 xmlrpcval

(

$v

'string'

)

;

    

}

    

$newparams

 = 

new

 xmlrpcval

(

$newparams

'struct'

)

;

    

return

 

new

 xmlrpcmsg

(

$method

array

(

$newparams

))

;

}

$filedata

 =

 // dane wejsciowe

$params

 = 

array

(

'data'

=

>

$filedata

'extension'

=

>

'pdf'

)

;

$msg

 = makeRequest

(

'generate'

$params

)

;

$client

 = 

new

 xmlrpc_client 

(

'http://192.168.20.192:8008'

)

;

$res

 = 

$client

-

>

send

(

$msg

)

;

$response

 = analyzeResponse

(

$res

)

;

if

(

$response

[

'code'

]

 == 200

){

 

$data

 = getDataItem

(

$response

'data'

)

;

 // zawartosc pliku po skonwertowaniu

}

else

{

    

echo

 

'Failed with code '

.

$response

[

'code'

]

.

', message 

"'

.

$response

[

'message'

]

;

}

background image

38

Demon konwersji

sierpień 2007

rozwiązania

39

Demon konwersji

www.lpmagazine.org

rozwiązania

Poniżej zamieszczone są przykładowe kody 
klienta oood w różnych językach programo-
wania;  są  to  tylko  najważniejsze  fragmenty 
kodu – kompletne przykłady można znaleźć 
w repozytorium svn.

Python

Przykładowy skrypt klienta w Pythonie mo-
że wyglądać następująco: 

import xmlrpclib,

base64. 

Ułatwieniem jest moduł oood_com-

mon  zawierający  klasy 

Request

  i 

Response

które  m.  in.  sprawdzają  poprawność  argu-
mentów i kodują/dekodują dane. Oto ten sam 
skrypt wykorzystujący moduł 

oood_common

.

Zastosowania

Oood doczekał się już paru praktycznych za-
stosowań również w środowiskach produk-
cyjnych:

•  generowanie raportów w systemie ERP5 

–  system  ERP5  ma  wbudowaną  możli-
wość  generowania  raportów  w  forma-
tach ODF na podstawie szablonów xml-
owych; dzięki współpracy z oood rapor-
ty  mogą  być  na  żądanie  użytkownika 
generowane także w formacie MS Offi-
ce, html
 czy pdf.

•  system  zarządzania  dokumentami  – 

w systemie ERP5-DMS każdy dokument 
w  jakimkolwiek  formacie  „biurowym” 
jest  natychmiast  po  jego  umieszczeniu 
w  systemie  konwertowany  przez  oood 
na trzy formaty:
–  plain text – co umożliwia jego indek-

sowanie przez systemową wyszuki-
warkę,

–  html – dzięki czemu możliwy jest pod-

gląd treści dokumentu w przeglądarce 
bez konieczności jego pobierania,

–  ODF  –  w  tym  formacie  dokument 

jest składowany w systemie.

 

Użytkownik,  który  chce  pobrać  doku-
ment z systemu może wybrać format, ja-
ki mu najbardziej odpowiada – ODF, MS 
czy  jakikolwiek  inny  (obecnie  oood  ob-
sługuje 111 formatów).

•  mail2print  –  jest  to  dedykowany  serwer 

pocztowy, który każdy odebrany e-mail 

otwiera i samoczynnie drukuje wszyst-
kie  załączone  do  maila  dokumenty, 
wykorzystując do tego celu możliwości 
oood. Powstał na potrzeby firmy otrzy-
mującej  drogą  mailową  znaczne  ilości 
zamówień, które muszą być wydruko-
wane.

W przygotowaniu 

– „multi-oood”

Niedługo  powinien  ujrzeć  światło  dzienne 
„multi-oood”  –  serwer  pośredniczący,  któ-
ry umożliwi jednoczesne korzystanie z wielu 
uruchomionych jednocześnie instancji oood. 
Znacznie usprawni to jego działanie w przy-
padku korzystania z oood przez wielu użyt-
kowników jednocześnie. 

getAllowed-
TargetItemList

lista formatów, na które 
może być skonwertowany 
dany dokument; parame-
trem może być typ dokumentu 
(

text

spreadsheet

), 

rozszerzenie 
(

doc

txt

) lub mimetype 

(np 

application/msword

)

generate

konwersja dokumentu 
na wybrany format

getmetadata

pobranie metadanych 
dokumentu

setmetadata

edycja metadanych dokumentu

printDocument

wydruk dokumentu na lokalnej 
drukarce serwera

Bartłomiej Górny jest prezesem i udzia-
łowcem ERP5 Polska sp. z o.o. - firmy zaj-
mującej się rozwojem i wdrażaniem aplika-
cji na bazie systemu ERP5 (platformy biz-
nesowej dostępnej na licencji GPL). 
Kontakt z autorem: bartek@erp5.pl

O autorze

Listing 4. 

C++

#

include

 

"XmlRpc.h"

 // xmlrpc++ library ver.0.7 - 

http://xmlrpcpp.sourceforge.net/

#

include

 

"Base64.h"

 // Base64 class from C++ Sockets Library - 

http://www.alhem.net/Sockets/index.html

#include 

<iostream>

using

 

namespace

 

XmlRpc

;

using

 

namespace

 

std

;

int

 

main

(

int

 

argc

char

*

 

argv

[])

 

{

    

FILE

*

 

infile

 

=

 

fopen

(

"test2.odt"

,

"r"

);

    

std

::

string

 

data

;

    

Base64

 

tmp_base

;

    

tmp_base

.

encode

(

infile

data

false

);

    

XmlRpcClient

 

client

(

"localhost"

8008

);

    

XmlRpcValue

 

result

;

    

XmlRpcValue

 

Args

;

    

Args

[

"data"

]

 

=

 

data

.

c_str

();

    

Args

[

"extension"

]

 

=

 

"pdf"

;

    

client

.

execute

(

"generate"

Args

result

);

    

std

::

cout

 

<<

 

"Oood result code: "

 

<<

 

result

[

0

]

 

<<

 

std

::

endl

;

    

std

::

string

 

dec_data

;

    

tmp_base

.

decode

(

result

[

1

][

"data"

]

dec_data

);

    

FILE

*

 

outfile

 

=

 

fopen

(

"test.pdf"

,

"wb"

);

    

const

 

char

*

 

outbuf

 

=

 

dec_data

.

c_str

();

    

fwrite

(

outbuf

sizeof

(

dec_data

[

0

])

dec_data

.

size

()-

1

outfile

);

    

fclose

(

outfile

);

    

return

 

0

;

}

 

Kody protokołu oood

200: 'OK',
401: 'requested method is not provided',
402: 'the document could not be processed',
403: 'timeout while processing document',
404: 'malformed request',
501: 'the server is restarting',
502: 'pool is empty, the server will be restarted',
503: 'the server is out of sync and will 
be restarted',
504: 'unknown error'

•  Strona domowa oood: 
 

http://wiki.erp5.org/
HowToUseOood

•  Repozytorium svn: 
 

http://svn.erp5.org/repos/
public/erp5/trunk/utils/oood

•  Lista dyskusyjna: 
 

erp5-dev@erp5.org

W Sieci