annotate soap.cpp @ 333:cc3f9d1ca2cd

Ugly hack upon ugly hack: * extend adb__query() to take a bunch of other arguments that allow useful functionality through to the SOAP server; * alter the RadiusQuery reporter so that the count for tracks is returned through SOAP, punning one of the result fields for the purpose; * alter argv construction to be more dynamic, to reflect that the very presence of some arguments changes audioDB behaviour. Now test 0020 passes, only some 12 months after it was originally written, yay. Alter test 0050 also, so that the results are actually vaguely what would be expected from a radius search; they are not completely checked for correctness, but are (apart from the last two) the same as test 0040. I believe that the modifications are backward compatible; lightly testing with an old audioDB binary suggests that old-format SOAP queries continue to work. Currently too baby-encumbered to think of how to test this fully.
author mas01cr
date Mon, 01 Sep 2008 15:35:05 +0000
parents 0c908093082f
children 100cf66a5825
rev   line source
mas01cr@239 1 #include "audioDB.h"
mas01cr@239 2 #include "adb.nsmap"
mas01cr@239 3
mas01cr@239 4 /* Command-line client definitions */
mas01cr@239 5
mas01cr@239 6 // FIXME: this can't propagate the sequence length argument (used for
mas01cr@239 7 // dudCount). See adb__status() definition for the other half of
mas01cr@239 8 // this. -- CSR, 2007-10-01
mas01cr@239 9 void audioDB::ws_status(const char*dbName, char* hostport){
mas01cr@239 10 struct soap soap;
mas01cr@239 11 adb__statusResponse adbStatusResponse;
mas01cr@239 12
mas01cr@239 13 // Query an existing adb database
mas01cr@239 14 soap_init(&soap);
mas01cr@239 15 if(soap_call_adb__status(&soap,hostport,NULL,(char*)dbName,adbStatusResponse)==SOAP_OK) {
mas01cr@239 16 std::cout << "numFiles = " << adbStatusResponse.result.numFiles << std::endl;
mas01cr@239 17 std::cout << "dim = " << adbStatusResponse.result.dim << std::endl;
mas01cr@239 18 std::cout << "length = " << adbStatusResponse.result.length << std::endl;
mas01cr@239 19 std::cout << "dudCount = " << adbStatusResponse.result.dudCount << std::endl;
mas01cr@239 20 std::cout << "nullCount = " << adbStatusResponse.result.nullCount << std::endl;
mas01mc@324 21 std::cout << "flags = " << (adbStatusResponse.result.flags & 0x00FFFFFF) << std::endl;
mas01cr@239 22 } else {
mas01cr@239 23 soap_print_fault(&soap,stderr);
mas01cr@239 24 }
mas01cr@239 25
mas01cr@239 26 soap_destroy(&soap);
mas01cr@239 27 soap_end(&soap);
mas01cr@239 28 soap_done(&soap);
mas01cr@239 29 }
mas01cr@239 30
mas01mc@308 31 // WS_QUERY (CLIENT SIDE)
mas01mc@307 32 void audioDB::ws_query(const char*dbName, const char *featureFileName, const char* hostport){
mas01cr@239 33 struct soap soap;
mas01cr@239 34 adb__queryResponse adbQueryResponse;
mas01mc@329 35 VERB_LOG(1, "Calling fileName query on database %s with featureFile=%s\n", dbName, featureFileName);
mas01cr@333 36 soap_init(&soap);
mas01cr@333 37 if(soap_call_adb__query(&soap, hostport, NULL, (char *) dbName,
mas01cr@333 38 (char *)featureFileName, (char *)trackFileName,
mas01cr@333 39 (char *)timesFileName, (char *) powerFileName,
mas01cr@333 40 queryType, queryPoint,
mas01cr@333 41 pointNN, trackNN, sequenceLength,
mas01cr@333 42 radius, absolute_threshold, relative_threshold,
mas01cr@333 43 !usingQueryPoint, lsh_exact,
mas01cr@333 44 adbQueryResponse)
mas01cr@333 45 == SOAP_OK) {
mas01cr@333 46 if(radius == 0) {
mas01cr@333 47 for(int i=0; i<adbQueryResponse.result.__sizeRlist; i++) {
mas01cr@333 48 std::cout << adbQueryResponse.result.Rlist[i] << " "
mas01cr@333 49 << adbQueryResponse.result.Dist[i] << " "
mas01cr@333 50 << adbQueryResponse.result.Qpos[i] << " "
mas01cr@333 51 << adbQueryResponse.result.Spos[i] << std::endl;
mas01cr@333 52 }
mas01cr@333 53 } else {
mas01cr@333 54 for(int i = 0; i < adbQueryResponse.result.__sizeRlist; i++) {
mas01cr@333 55 std::cout << adbQueryResponse.result.Rlist[i] << " "
mas01cr@333 56 << adbQueryResponse.result.Spos[i] << std::endl;
mas01cr@333 57 }
mas01cr@333 58 }
mas01cr@333 59 } else {
mas01cr@333 60 soap_print_fault(&soap,stderr);
mas01cr@239 61 }
mas01cr@333 62
mas01cr@239 63 soap_destroy(&soap);
mas01cr@239 64 soap_end(&soap);
mas01cr@239 65 soap_done(&soap);
mas01cr@239 66 }
mas01mc@307 67
mas01mc@308 68 // WS_QUERY_BY_KEY (CLIENT SIDE)
mas01mc@328 69 void audioDB::ws_query_by_key(const char*dbName, const char *trackKey, const char* featureFileName, const char* hostport){
mas01mc@307 70 struct soap soap;
mas01mc@307 71 adb__queryResponse adbQueryResponse;
mas01mc@314 72 /* JUST TRY TO USE A DATA STRUCTURE WITH PHP
mas01mc@314 73 adb__sequenceQueryParms asqp;
mas01mc@314 74 asqp.keyList = (char*)trackFileName;
mas01mc@314 75 asqp.timesFileName = (char*)timesFileName;
mas01mc@314 76 asqp.queryPoint = queryPoint;
mas01mc@314 77 asqp.pointNN = pointNN;
mas01mc@314 78 asqp.trackNN = trackNN;
mas01mc@314 79 asqp.sequenceLength = sequenceLength;
mas01mc@314 80 asqp.radius = radius;
mas01mc@314 81 asqp.relative_threshold = relative_threshold;
mas01mc@314 82 asqp.absolute_threshold = absolute_threshold;
mas01mc@314 83 asqp.usingQueryPoint = usingQueryPoint;
mas01mc@314 84 asqp.lsh_exact = lsh_exact;
mas01mc@314 85 */
mas01mc@331 86 VERB_LOG(1, "Calling %s query on database %s with %s=%s\n", (trackKey&&strlen(trackKey))?"KEY":"FILENAME", dbName, (trackKey&&strlen(trackKey))?"KEY":"FILENAME",(trackKey&&strlen(trackKey))?trackKey:featureFileName);
mas01mc@307 87 soap_init(&soap);
mas01mc@307 88 if(queryType==O2_SEQUENCE_QUERY || queryType==O2_N_SEQUENCE_QUERY){
mas01mc@314 89 if(soap_call_adb__sequenceQueryByKey(&soap,hostport,NULL,
mas01mc@328 90 (char*)dbName,
mas01mc@328 91 (char*)trackKey,
mas01mc@328 92 (char*)featureFileName,
mas01mc@328 93 queryType,
mas01mc@328 94 (char*)trackFileName, // this means keyFileName
mas01mc@328 95 (char*)timesFileName,
mas01mc@314 96 queryPoint,
mas01mc@328 97 pointNN,
mas01mc@328 98 trackNN,
mas01mc@328 99 sequenceLength,
mas01mc@328 100 radius,
mas01mc@328 101 absolute_threshold,
mas01mc@328 102 usingQueryPoint,
mas01mc@314 103 lsh_exact,
mas01mc@314 104 adbQueryResponse)==SOAP_OK){
mas01mc@307 105 //std::std::cerr << "result list length:" << adbQueryResponse.result.__sizeRlist << std::std::endl;
mas01mc@307 106 for(int i=0; i<adbQueryResponse.result.__sizeRlist; i++)
mas01mc@307 107 std::cout << adbQueryResponse.result.Rlist[i] << " " << adbQueryResponse.result.Dist[i]
mas01mc@307 108 << " " << adbQueryResponse.result.Qpos[i] << " " << adbQueryResponse.result.Spos[i] << std::endl;
mas01mc@307 109 }
mas01mc@307 110 else
mas01mc@307 111 soap_print_fault(&soap,stderr);
mas01mc@307 112 }else
mas01mc@307 113 ;// FIX ME: WRITE NON-SEQUENCE QUERY BY KEY ?
mas01mc@307 114
mas01mc@307 115 soap_destroy(&soap);
mas01mc@307 116 soap_end(&soap);
mas01mc@307 117 soap_done(&soap);
mas01mc@307 118 }
mas01mc@307 119
mas01cr@239 120
mas01cr@333 121 /* handy macros */
mas01cr@333 122 #define INTSTRINGIFY(val, str) \
mas01cr@333 123 char str[256]; \
mas01cr@333 124 snprintf(str, 256, "%d", val);
mas01cr@333 125 #define DOUBLESTRINGIFY(val, str) \
mas01cr@333 126 char str[256]; \
mas01cr@333 127 snprintf(str, 256, "%f", val);
mas01cr@333 128
mas01cr@239 129 /* Server definitions */
mas01cr@239 130 int adb__status(struct soap* soap, xsd__string dbName, adb__statusResponse &adbStatusResponse){
mas01mc@307 131 char* const argv[]={"./audioDB",COM_STATUS,"-d",dbName};
mas01cr@239 132 const unsigned argc = 4;
mas01cr@239 133 try {
mas01cr@239 134 audioDB(argc, argv, &adbStatusResponse);
mas01cr@239 135 return SOAP_OK;
mas01cr@239 136 } catch(char *err) {
mas01cr@239 137 soap_receiver_fault(soap, err, "");
mas01cr@239 138 return SOAP_FAULT;
mas01cr@239 139 }
mas01cr@239 140 }
mas01mc@308 141
mas01cr@239 142 // Literal translation of command line to web service
mas01cr@333 143 int adb__query(struct soap* soap, xsd__string dbName,
mas01cr@333 144 xsd__string qKey, xsd__string keyList,
mas01cr@333 145 xsd__string timesFileName, xsd__string powerFileName,
mas01cr@333 146 xsd__int qType,
mas01cr@333 147 xsd__int qPos, xsd__int pointNN, xsd__int trackNN,
mas01cr@333 148 xsd__int seqLen,
mas01cr@333 149 xsd__double radius,
mas01cr@333 150 xsd__double absolute_threshold, xsd__double relative_threshold,
mas01cr@333 151 xsd__int exhaustive, xsd__int lsh_exact,
mas01cr@333 152 adb__queryResponse &adbQueryResponse){
mas01cr@239 153 char queryType[256];
mas01mc@329 154
mas01mc@329 155 fprintf(stderr,"Calling fileName query on database %s with featureFile=%s\n", dbName, qKey);
mas01mc@329 156
mas01cr@239 157 for(int k=0; k<256; k++)
mas01cr@239 158 queryType[k]='\0';
mas01cr@239 159 if(qType == O2_POINT_QUERY)
mas01cr@239 160 strncpy(queryType, "point", strlen("point"));
mas01cr@239 161 else if (qType == O2_SEQUENCE_QUERY)
mas01cr@239 162 strncpy(queryType, "sequence", strlen("sequence"));
mas01cr@239 163 else if(qType == O2_TRACK_QUERY)
mas01cr@239 164 strncpy(queryType,"track", strlen("track"));
mas01mc@324 165 else if(qType == O2_N_SEQUENCE_QUERY)
mas01mc@324 166 strncpy(queryType,"nsequence", strlen("nsequence"));
mas01cr@239 167
mas01cr@239 168 if(pointNN==0)
mas01cr@239 169 pointNN=10;
mas01cr@239 170 if(trackNN==0)
mas01cr@239 171 trackNN=10;
mas01cr@239 172 if(seqLen==0)
mas01cr@239 173 seqLen=16;
mas01cr@239 174
mas01cr@333 175 INTSTRINGIFY(qPos, qPosStr);
mas01cr@333 176 INTSTRINGIFY(pointNN, pointNNStr);
mas01cr@333 177 INTSTRINGIFY(trackNN, trackNNStr);
mas01cr@333 178 INTSTRINGIFY(seqLen, seqLenStr);
mas01cr@239 179
mas01cr@333 180 /* We don't necessarily use these, but because of scope we do this
mas01cr@333 181 anyway. We waste 756 bytes of stack this way. */
mas01cr@333 182 DOUBLESTRINGIFY(radius, radiusStr);
mas01cr@333 183 DOUBLESTRINGIFY(absolute_threshold, absolute_thresholdStr);
mas01cr@333 184 DOUBLESTRINGIFY(relative_threshold, relative_thresholdStr);
mas01cr@333 185
mas01cr@333 186 unsigned int argc = 19;
mas01cr@333 187 if (powerFileName) {
mas01cr@333 188 argc += 2;
mas01cr@333 189 }
mas01cr@333 190 if (radius != 0) {
mas01cr@333 191 argc += 2;
mas01cr@333 192 }
mas01cr@333 193 /* we can't use use_absolute_threshold and friends because we're not
mas01cr@333 194 in the audioDB class here. */
mas01cr@333 195 if (absolute_threshold != 0) {
mas01cr@333 196 argc += 2;
mas01cr@333 197 }
mas01cr@333 198 if (relative_threshold != 0) {
mas01cr@333 199 argc += 2;
mas01cr@333 200 }
mas01cr@333 201 if (exhaustive) {
mas01cr@333 202 argc++;
mas01cr@333 203 }
mas01cr@333 204 if (lsh_exact) {
mas01cr@333 205 argc++;
mas01cr@333 206 }
mas01cr@333 207
mas01cr@333 208 char **argv = new char*[argc+1];
mas01cr@333 209 argv[0] = "./audioDB";
mas01cr@333 210 argv[1] = COM_QUERY;
mas01cr@333 211 argv[2] = queryType;
mas01cr@333 212 argv[3] = COM_DATABASE;
mas01cr@333 213 argv[4] = (char *) (ENSURE_STRING(dbName));
mas01cr@333 214 argv[5] = COM_FEATURES;
mas01cr@333 215 argv[6] = (char *) (ENSURE_STRING(qKey));
mas01cr@333 216 argv[7] = COM_KEYLIST;
mas01cr@333 217 argv[8] = (char *) (ENSURE_STRING(keyList));
mas01cr@333 218 argv[9] = COM_TIMES;
mas01cr@333 219 argv[10] = (char *) (ENSURE_STRING(timesFileName));
mas01cr@333 220 argv[11] = COM_QPOINT;
mas01cr@333 221 argv[12] = qPosStr;
mas01cr@333 222 argv[13] = COM_POINTNN;
mas01cr@333 223 argv[14] = pointNNStr;
mas01cr@333 224 argv[15] = COM_TRACKNN;
mas01cr@333 225 argv[16] = trackNNStr;
mas01cr@333 226 argv[17] = COM_SEQLEN;
mas01cr@333 227 argv[18] = seqLenStr;
mas01cr@333 228 int argv_counter = 19;
mas01cr@333 229 if (powerFileName) {
mas01cr@333 230 argv[argv_counter++] = COM_QUERYPOWER;
mas01cr@333 231 argv[argv_counter++] = powerFileName;
mas01cr@333 232 }
mas01cr@333 233 if (radius != 0) {
mas01cr@333 234 argv[argv_counter++] = COM_RADIUS;
mas01cr@333 235 argv[argv_counter++] = radiusStr;
mas01cr@333 236 }
mas01cr@333 237 if (absolute_threshold != 0) {
mas01cr@333 238 argv[argv_counter++] = COM_ABSOLUTE_THRESH;
mas01cr@333 239 argv[argv_counter++] = absolute_thresholdStr;
mas01cr@333 240 }
mas01cr@333 241 if (relative_threshold != 0) {
mas01cr@333 242 argv[argv_counter++] = COM_RELATIVE_THRESH;
mas01cr@333 243 argv[argv_counter++] = relative_thresholdStr;
mas01cr@333 244 }
mas01cr@333 245 if (exhaustive) {
mas01cr@333 246 argv[argv_counter++] = COM_EXHAUSTIVE;
mas01cr@333 247 }
mas01cr@333 248 if (lsh_exact) {
mas01cr@333 249 argv[argv_counter++] = COM_LSH_EXACT;
mas01cr@333 250 }
mas01cr@333 251 argv[argv_counter] = NULL;
mas01cr@333 252
mas01cr@333 253
mas01cr@239 254 try {
mas01cr@239 255 audioDB(argc, (char* const*)argv, &adbQueryResponse);
mas01cr@333 256 delete [] argv;
mas01cr@239 257 return SOAP_OK;
mas01cr@239 258 } catch (char *err) {
mas01cr@239 259 soap_receiver_fault(soap, err, "");
mas01cr@333 260 delete [] argv;
mas01cr@239 261 return SOAP_FAULT;
mas01cr@239 262 }
mas01cr@239 263 }
mas01cr@239 264
mas01mc@314 265 int adb__sequenceQueryByKey(struct soap* soap,xsd__string dbName,
mas01mc@314 266 xsd__string trackKey,
mas01mc@328 267 xsd__string featureFileName,
mas01mc@314 268 xsd__int queryType,
mas01mc@328 269 xsd__string keyFileName,
mas01mc@314 270 xsd__string timesFileName,
mas01mc@314 271 xsd__int queryPoint,
mas01mc@314 272 xsd__int pointNN,
mas01mc@314 273 xsd__int trackNN,
mas01mc@314 274 xsd__int sequenceLength,
mas01mc@314 275 xsd__double radius,
mas01mc@314 276 xsd__double absolute_threshold,
mas01mc@314 277 xsd__int usingQueryPoint,
mas01mc@314 278 xsd__int lsh_exact,
mas01mc@314 279 struct adb__queryResponse& adbQueryResponse){
mas01mc@307 280 char qtypeStr[256];
mas01cr@239 281
mas01mc@331 282 fprintf(stderr, "Calling %s query on database %s with %s=%s\n", (trackKey&&strlen(trackKey))?"KEY":"FILENAME", dbName, (trackKey&&strlen(trackKey))?"KEY":"FILENAME",(trackKey&&strlen(trackKey))?trackKey:featureFileName);
mas01mc@329 283
mas01mc@314 284 INTSTRINGIFY(queryPoint, qPosStr);
mas01mc@314 285 INTSTRINGIFY(pointNN, pointNNStr);
mas01mc@314 286 INTSTRINGIFY(trackNN, trackNNStr);
mas01mc@314 287 INTSTRINGIFY(sequenceLength, seqLenStr);
mas01mc@314 288 DOUBLESTRINGIFY(absolute_threshold, absolute_thresholdStr);
mas01mc@314 289 DOUBLESTRINGIFY(radius, radiusStr);
mas01mc@307 290
mas01mc@307 291 // WS queries only support 1-nearest neighbour point reporting
mas01mc@307 292 // at the moment, until we figure out how to better serve results
mas01mc@307 293 snprintf(qtypeStr, 256, "nsequence");
mas01mc@310 294 const char *argv[]={
mas01cr@239 295 "./audioDB",
mas01cr@239 296 COM_QUERY,
mas01mc@307 297 qtypeStr,
mas01cr@239 298 COM_DATABASE,
mas01cr@239 299 dbName,
mas01mc@330 300 (trackKey&&strlen(trackKey))?COM_QUERYKEY:COM_FEATURES,
mas01mc@330 301 (trackKey&&strlen(trackKey))?ENSURE_STRING(trackKey):ENSURE_STRING(featureFileName),
mas01cr@239 302 COM_KEYLIST,
mas01mc@328 303 ENSURE_STRING(keyFileName),
mas01mc@314 304 usingQueryPoint?COM_QPOINT:COM_EXHAUSTIVE,
mas01mc@314 305 usingQueryPoint?qPosStr:"",
mas01cr@239 306 COM_POINTNN,
mas01cr@239 307 pointNNStr,
mas01cr@239 308 COM_TRACKNN,
mas01cr@239 309 trackNNStr,
mas01mc@307 310 COM_RADIUS,
mas01mc@307 311 radiusStr,
mas01cr@239 312 COM_SEQLEN,
mas01cr@239 313 seqLenStr,
mas01cr@239 314 COM_ABSOLUTE_THRESH,
mas01mc@310 315 absolute_thresholdStr,
mas01mc@314 316 lsh_exact?COM_LSH_EXACT:""
mas01cr@239 317 };
mas01cr@239 318
mas01mc@310 319 const unsigned argc = 22;
mas01mc@310 320
mas01mc@310 321
mas01cr@239 322 try {
mas01cr@239 323 audioDB(argc, (char* const*)argv, &adbQueryResponse);
mas01cr@239 324 return SOAP_OK;
mas01cr@239 325 } catch (char *err) {
mas01cr@239 326 soap_receiver_fault(soap, err, "");
mas01cr@239 327 return SOAP_FAULT;
mas01cr@239 328 }
mas01cr@239 329 }
mas01cr@239 330
mas01cr@239 331 /* Server loop */
mas01cr@239 332 void audioDB::startServer(){
mas01cr@239 333 struct soap soap;
mas01cr@239 334 int m, s; // master and slave sockets
mas01cr@239 335 soap_init(&soap);
mas01cr@239 336 // FIXME: largely this use of SO_REUSEADDR is to make writing (and
mas01cr@239 337 // running) test cases more convenient, so that multiple test runs
mas01cr@239 338 // in close succession don't fail because of a bin() error.
mas01cr@239 339 // Investigate whether there are any potential drawbacks in this,
mas01cr@239 340 // and also whether there's a better way to write the tests. --
mas01cr@239 341 // CSR, 2007-10-03
mas01cr@239 342 soap.bind_flags |= SO_REUSEADDR;
mas01cr@239 343 m = soap_bind(&soap, NULL, port, 100);
mas01cr@239 344 if (m < 0)
mas01cr@239 345 soap_print_fault(&soap, stderr);
mas01cr@239 346 else
mas01cr@239 347 {
mas01cr@239 348 fprintf(stderr, "Socket connection successful: master socket = %d\n", m);
mas01mc@308 349 // Make a global Web Services LSH Index (SINGLETON)
mas01mc@313 350 if(WS_load_index && dbName && !index_exists(dbName, radius, sequenceLength)){
mas01mc@313 351 error("Can't find requested index file:", index_get_name(dbName,radius,sequenceLength));
mas01mc@313 352 }
mas01mc@308 353 if(WS_load_index && dbName && index_exists(dbName, radius, sequenceLength)){
mas01mc@308 354 char* indexName = index_get_name(dbName, radius, sequenceLength);
mas01mc@308 355 fprintf(stderr, "Loading LSH hashtables: %s...\n", indexName);
mas01mc@308 356 lsh = new LSH(indexName, true);
mas01mc@308 357 assert(lsh);
mas01mc@308 358 SERVER_LSH_INDEX_SINGLETON = lsh;
mas01mc@308 359 fprintf(stderr, "LSH INDEX READY\n");
mas01mc@308 360 fflush(stderr);
mas01mc@308 361 delete[] indexName;
mas01mc@308 362 }
mas01mc@324 363
mas01mc@324 364 // Server-side path prefix to databases and features
mas01mc@324 365 if(adb_root)
mas01mc@324 366 SERVER_ADB_ROOT = (char*)adb_root; // Server-side database root
mas01mc@324 367 if(adb_feature_root)
mas01mc@324 368 SERVER_ADB_FEATURE_ROOT = (char*)adb_feature_root; // Server-side features root
mas01mc@308 369
mas01cr@239 370 for (int i = 1; ; i++)
mas01cr@239 371 {
mas01cr@239 372 s = soap_accept(&soap);
mas01cr@239 373 if (s < 0)
mas01cr@239 374 {
mas01cr@239 375 soap_print_fault(&soap, stderr);
mas01cr@239 376 break;
mas01cr@239 377 }
mas01cr@239 378 /* FIXME: find a way to play nice with logging when run from
mas01cr@239 379 /etc/init.d scripts: at present this just goes nowhere */
mas01cr@239 380 fprintf(stderr, "%d: accepted connection from IP=%lu.%lu.%lu.%lu socket=%d\n", i,
mas01cr@239 381 (soap.ip >> 24)&0xFF, (soap.ip >> 16)&0xFF, (soap.ip >> 8)&0xFF, soap.ip&0xFF, s);
mas01cr@239 382 if (soap_serve(&soap) != SOAP_OK) // process RPC request
mas01cr@239 383 soap_print_fault(&soap, stderr); // print error
mas01cr@239 384 fprintf(stderr, "request served\n");
mas01cr@239 385 soap_destroy(&soap); // clean up class instances
mas01cr@239 386 soap_end(&soap); // clean up everything and close socket
mas01cr@239 387 }
mas01cr@239 388 }
mas01cr@239 389 soap_done(&soap); // close master socket and detach environment
mas01cr@239 390 }