Forum OpenACS Q&A: Tcl Web Service

Collapse
Posted by Michal Hanckowiak on
Does anybody know how to use "tclws" package from OpenACS?
tclws is a WebService/SOAP/WSDL toolkit available from:
http://core.tcl.tk/tclws/doc/Release_2.3.7/docs/index.html
http://core.tcl.tk/tclws/wiki?name=Downloads
There is (in tclws package) an example how to use it form pure AOLserver, but I dont know how to do it from OpenACS ...

regards,
Michal Hanckowiak

Collapse
2: Re: Tcl Web Service (response to 1)
Posted by Michal Hanckowiak on
I must add that I want to use tclws as a server (WebService implementation),
and that it should use the same port number as that used by oacs,
(so it can not be in external process or "helper thread")

MH

Collapse
3: Re: Tcl Web Service (response to 2)
Posted by Gustaf Neumann on
Hi Michal,

If the package works for AOLserver that it should no big problem to get it working under OpenACS. There might be differences with rights management or request processor, bit these should be solvable. Does it work for you with plain AOLserver?

You might check out xotcl-soap
http://alice.wu-wien.ac.at:8000/xorb-doc/
https://openacs.org/forums/message-view?message_id=1392605
which provides client and server side. I've not done anything, but as far as i know, it was used in an EU project at UNED.

best regards
-gn

Collapse
4: Re: Tcl Web Service (response to 2)
Posted by Brian Fenton on
Hi Michal

I don't envy you - the world of SOAP is a world of pain! I've heard that xotcl-soap is a good solution, but I haven't tried it. I have used the TCL SOAP package from within OpenACS http://tclsoap.sourceforge.net/, but as a client not a server. I found it a well written and useful package, but as I said, I found the SOAP world very painful, especially when dealing with Microsoft services. I believe that REST is far more popular these days. See for example https://stackoverflow.com/questions/76595/soap-or-rest-for-web-services?rq=1

Once you have everything compiled properly, you just need to add these lines in /web/server/tcl/0-acs-init.tcl

package require SOAP
package require SOAP::https
#package require SOAP::WSDL
package require tls
package require tdom

good luck!
Brian

Collapse
5: Re: Tcl Web Service (response to 4)
Posted by Michal Hanckowiak on
Thanks Gustaf and Brian for your answers ;);

tclws toolkit is working perfectly for me as a standalne serwer, I didtn tested it under AOLserver, but I will try to do it...

I know about tclsoap package...
unfortunately, it is a very old package and it is using only old "rpc/encoded" style of WebService,
this is the reason why it does not work with
contemporary webservices using "document/literal" style;

tclws is much more modern, is using document/literal style,
and it automatically generates WSDL files...
I tested it with a good result, with WS toolkits:
java/Axis 1, gSOAP, http://www.webservicex.net/globalweather.asmx, ...

I know that WS/REST is simpler than WS/SOAP/WSDL but
the second also has some advantages,
for example under java/axis it is very easy to use a webservice by its wsdl file (authomatically generated java stub)

....................

best regards,
Michal Hanckowiak

Collapse
6: Re: Tcl Web Service (response to 1)
Posted by Michal Hanckowiak on
Hi,

It appeared very easy to integrate tclws and oacs...

I use (logical) tcl interp because tclws uses global variables.
Note that WS definition is done only during the first http request
to that webservice (probably when wsdl is read by client toolkit).
Of course tclws tcl package must be loadable for oacs...

If we want to install WS "wsexample" in oacs package "qqq1",
then we should put file below in qqq1/www:

----------------------------------------
## using tclws from oacs (wsexample.vuh file)
#

#catch {interp delete tclws_qqq1_wsexample}; # dbg

proc tclws_eval args {uplevel $args}

if {![interp exists tclws_qqq1_wsexample]} {
interp create tclws_qqq1_wsexample
# name of an interp: tclws_oacsPackageName_WSName
# "qqq1" is a name of oacs package with WS "wsexample"

tclws_qqq1_wsexample alias x tclws_eval
tclws_qqq1_wsexample alias ns_return ns_return

tclws_qqq1_wsexample eval {
# only at the beginning

set prefix "/przyklad1/qqq1/wsexample"
set service "wsexample"
set host "myserver:myportnumber"

proc log {x y} {x ns_log $x $y}
namespace eval log {}
proc log::log {x y} {x ns_log $x $y}

package re WS::Server
package re WS::AOLserver

::WS::Server::Service -mode aolserver \
-prefix $prefix -service $service -host $host \
-description {oacs + tclws example}

::WS::Server::ServiceProc $service {razyDwa {type int()}} {
par1 {type int() comment {a sequence of ints}}
} {multiply by 2 an input sequence of ints} {
set w {}
foreach e $par1 {lappend w [expr {$e*2}]}
return [list razyDwaResult $w]
}

log notice "WebService $service definition..."

}
}
tclws_qqq1_wsexample eval {
# for every http request

set reqType [x ad_conn path_info]
if {$reqType=="/"||$reqType==""} {set reqType "/doc"}

switch -exact -- $reqType {
/doc {
WS::Server::generateInfo $service nosock
}
/wsdl {
WS::Server::generateWsdl $service nosock
log notice "WS::Server::generateWsdl $service nosock"
}
/op {
upvar #0 Httpdnosock data
set data(ipaddr) [x ad_conn peeraddr]
set data(headerlist) [x ns_set array [x ad_conn headers]]
set data(query) [x ad_conn content]
WS::Server::callOperation $service nosock
log notice "WS::Server::callOperation $service nosock"
}
}
}

----------------------------------------

And here is an example how to use that webserwise
from client-side of tclws toolkit:

-------------------------------------

set headers "Cookie ad_session_id=???"
# + ad_session_id must be somehow reveived (?)

package re WS::Client
#% 2.3.7

set w1 [::WS::Client::GetAndParseWsdl http://myserver:myportnumber/przyklad1/qqq1/wsexample/wsdl $headers]

dict get $w1 name
#% wsexample
# + this is a name of our WS

::WS::Client::CreateStubs wsexample
#%
::wsexample::razyDwa par1

wsexample::razyDwa {1 2 3 4 5 6}
#% razyDwaResult {2 4 6 8 10 12}

--------------------------------------------

...............
best regards,
Michal Hanckowiak

Collapse
7: Re: Tcl Web Service (response to 6)
Posted by Michal Hanckowiak on
more compact version of the code above...

if tclws_definition proc is defined somewhere:
------------------------------------------
proc tclws_eval args {uplevel $args}
proc tclws_definition code {
set p1 [ad_conn url]
set p2 [ad_conn path_info]
set p3 [string range $p1 0 end-[string len $p2]]
set p4 [lindex [split $p3 /] end]
set service $p4
set prefix $p3
set host "[ad_host][ad_port]"
set interpName tclws_[ad_conn package_key]_$service
if {![interp exists $interpName]} {
interp create $interpName
$interpName alias x tclws_eval
$interpName alias ns_return ns_return
$interpName eval set service $service
$interpName eval set prefix $prefix
$interpName eval set host $host
$interpName eval {
proc log {x y} {x ns_log $x $y}
namespace eval log {}
proc log::log {x y} {x ns_log $x $y}
package re WS::Server
package re WS::AOLserver
}
$interpName eval $code
}
$interpName eval {
set reqType [x ad_conn path_info]
if {$reqType=="/"||$reqType==""} {set reqType "/doc"}
switch -exact -- $reqType {
/doc {
WS::Server::generateInfo $service nosock
log notice "WS::Server::generateInfo $service nosock"
}
/wsdl {
WS::Server::generateWsdl $service nosock
log notice "WS::Server::generateWsdl $service nosock"
}
/op {
upvar #0 Httpdnosock data
set data(ipaddr) [x ad_conn peeraddr]
set data(headerlist) [x ns_set array [x ad_conn headers]]
set data(query) [x ad_conn content]
WS::Server::callOperation $service nosock
log notice "WS::Server::callOperation $service nosock"
}
}
}
}
------------------------------------------

then .vuh file becomes very simple:
------------------------------------------
tclws_definition {
::WS::Server::Service -mode aolserver \
-prefix $prefix -service $service -host $host \
-description {oacs + tclws example}

::WS::Server::ServiceProc $service {razyDwa {type int()}} {
par1 {type int() comment {ciag liczb int}}
} {mnozy elementy ciagu razy 2} {
set w {}
foreach e $par1 {lappend w [expr {$e*2}]}
return [list razyDwaResult $w]
}

::WS::Server::ServiceProc $service {razyTrzy {type int()}} {
par1 {type int() comment {ciag liczb int}}
} {mnozy elementy ciagu razy 3} {
set w {}
foreach e $par1 {lappend w [expr {$e*3}]}
return [list razyTrzyResult $w]
}

}
------------------------------------------