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
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
i
./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
:
'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'
]
;
}
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