summaryrefslogtreecommitdiff
path: root/TAO/IIOP/test/test1_svr.cpp
blob: c4a51a036c792db65bf118d8e1c91136e6deeee5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
// @(#)test1_svr.cpp	1.7 95/09/25
// Copyright 1995 by Sun Microsystems Inc.
// All Rights Reserved
//
// TEST:	simple IIOP server for "test1.idl" interface.
//
// Starts up, builds an objref, prints its string, listens for
// messages, responds to them.
//

#include	<stdio.h>
#include	<string.h>
#include <ace/Get_Opt.h>

#if	unix
#	include	<unistd.h>		// for getopt on some systems

#else	// windows

#endif

#include	"test1.h"
#include	<corba/boa.h>
#include <roa.h>

#include	<corba/debug.h>



#if !defined (_WIN32)
extern char 	*optarg;	// missing on some platforms
#endif

//
// Skeleton code ... just a macro for a bunch of DSI-based method code,
// in lieu of having an IDL compmiler generate static skeletons.  Static
// skeletons would be more efficient; most mallocation could go away.
//
// Use by:  defining OPERATION macro, call DEFINE_SKEL3 as needed, then
// undef OPERATION.
//
// NOTE:  "v1_copy" below is needed to work around a bug with the
// HP9000 G++ 2.6.3 compiler, with "LongLong".
//
// XXX this could probably be a template ... or could even be merged
// directly into the Dynamic Implementation Routine below.
//
// XXX we must currently use IN_COPY_VALUE since the memory consumed
// by the parameters must be deallocated by the ORB.  When we get an
// updated version of DSI which provides "send it now" semantics,
// these should preallocate the values and not use IN_COPY_VALUE.  A
// net decrease in malloc overhead can be had that way.  (NVList should
// also get a public constructor, and a way to provide the buffer.)
//
#define	DEFINE_SKEL3(name,truetype,truetypename) \
    static void \
    _test1_test_ ## name ( \
	CORBA_ServerRequest	&req, \
        CORBA_Object_ptr        obj, \
	CORBA_Environment	&env \
    ) \
    { \
	CORBA_NVList_ptr	nvlist; \
	CORBA_ ## truetype	scratch = 0; \
	CORBA_Any		temp_value (_tc_CORBA_ ## truetypename, \
				    &scratch, CORBA_B_FALSE); \
 	\
	req.orb ()->create_list (3, nvlist); \
	(void) nvlist->add_value (0, temp_value, \
		CORBA_IN_COPY_VALUE|CORBA_ARG_IN, env); \
	(void) nvlist->add_value (0, temp_value, \
		CORBA_IN_COPY_VALUE|CORBA_ARG_OUT, env); \
	(void) nvlist->add_value (0, temp_value, \
		CORBA_IN_COPY_VALUE|CORBA_ARG_INOUT, env); \
	 \
	req.params (nvlist, env); \
	if (env.exception () != 0) { \
	    dexc (env, "test1_test_" # name "skeleton, req.params"); \
	    return; \
	} \
 	\
	CORBA_ ## truetype 	*v1, *v2, *retval; \
 	\
	v1 = (CORBA_ ## truetype *) nvlist->item (0)->value ()->value (); \
	v2 = new CORBA_ ## truetype; \
	*v2 = (CORBA_ ## truetype) OPERATION (*v1); \
	retval = new CORBA_ ## truetype; \
	*retval = (CORBA_ ## truetype) OPERATION (*v1); \
 	\
	CORBA_Any_ptr		any_val; \
 	\
	any_val = nvlist->item (1)->value (); \
	any_val->replace (any_val->type (), v2, CORBA_B_TRUE, env); \
	if (env.exception () != 0) { \
	    dexc (env, "test1_test_" # name "skeleton, val2 replace"); \
	    return; \
	} \
 	\
	v1 = (CORBA_ ## truetype *) nvlist->item (2)->value ()->value (); \
    	CORBA_ ## truetype v1copy = *v1; \
	*v1 = (CORBA_ ## truetype) OPERATION (v1copy); \
 	\
	any_val = new CORBA_Any (_tc_CORBA_ ## truetypename, \
		retval, CORBA_B_TRUE); \
	req.result (any_val, env); \
	if (env.exception () != 0) { \
	    dexc (env, "test1_test_" # name "skeleton, result"); \
	    return; \
	} \
    } \
    extern TAO_Call_Data test1_ ## name ## _calldata;


extern const TAO_Call_Data test1_void_calldata;

static void
_test1_test_void (CORBA_ServerRequest &req,
		  CORBA_Object_ptr obj,
		  CORBA_Environment &env)
{
  CORBA_NVList_ptr nvlist;

  req.orb ()->create_list (0, nvlist);
  req.params (nvlist, env);

  if (env.exception () != 0)
    dexc (env, "test_throw, get params");
}


//
// Dynamic Skeleton methods for numeric types ... these all just
// cube their parameters in various permutations
//
template <class Type>
Type cube (Type arg)
{
    Type temp = arg;

    temp = temp * arg;
    temp = temp * arg;
    return temp;
}

#define	OPERATION(n)	cube(n)

DEFINE_SKEL3 (octet, Octet, Octet)

DEFINE_SKEL3 (short, Short, Short)
DEFINE_SKEL3 (ushort, UShort, UShort)

DEFINE_SKEL3 (long, Long, Long)
DEFINE_SKEL3 (ulong, ULong, ULong)

#if	!defined (NONNATIVE_LONGLONG)
	// don't try this on platforms that don't support
	// math on longlongs ...
DEFINE_SKEL3 (longlong, LongLong, LongLong)
DEFINE_SKEL3 (ulonglong, ULongLong, ULongLong)
#endif	// !NONNATIVE_LONGLONG

DEFINE_SKEL3 (float, Float, Float)
DEFINE_SKEL3 (double, Double, Double)

#if	!defined (NONNATIVE_LONGDOUBLE)
	// don't try this on platforms that don't support
	// math on long doubles ...
DEFINE_SKEL3 (longdouble, LongDouble, LongDouble)
#endif	// !NONNATIVE_LONGDOUBLE

#undef	OPERATION

//
// Dynamic Skeleton methods for Boolean type ... just negates its
// parameters 
//
#define	OPERATION(x)	(!(x))
DEFINE_SKEL3 (boolean, Boolean, Boolean)
#undef	OPERATION

//
// For character types, output is same as input, no magic needed.
//
#define	OPERATION(x)	(x)
DEFINE_SKEL3 (char, Char, Char)
DEFINE_SKEL3 (wchar, WChar, WChar)
#undef	OPERATION

//
// For objref, typecode, output is same as input but duplication
// is needed
//
#define	OPERATION(x)	((x) ? (x)->AddRef() : 0, x)
DEFINE_SKEL3 (Object, Object_ptr, Object)
DEFINE_SKEL3 (TypeCode, TypeCode_ptr, TypeCode)
#undef	OPERATION

//
// For string, output is copy of input
//
#define	OPERATION(x)	(CORBA_string_copy(x))
DEFINE_SKEL3 (string, String, String)
#undef	OPERATION

//
// For wstring, output is copy of input
//
#define	OPERATION(x)	(CORBA_wstring_copy(x))
DEFINE_SKEL3 (wstring, WString, WString)
#undef	OPERATION

//
// XXX IMPLEMENT THE REST OF THE DATA TYPES
//
// any, principal -- out is in
//


//
// All cases, "case_num" in the exception is the same as the 'in' param
//	* negative or zero, throws x1
//	* positive even cases, throws x2 with obj = null objref
//	* positive odd cases, throws x2 with obj = target objref
//
// exception x1 { long case_num; };
// exception x2 { Object obj; long case_num; };
// 
// void test_throw (in long case_num) raises (x1, x2);
//

extern const TAO_Call_Data test1_test_throw_calldata;

static void
_test1_test_throw (CORBA_ServerRequest &req,
		   CORBA_Object_ptr obj,
		   CORBA_Environment &env)
{
  CORBA_NVList_ptr nvlist;
  CORBA_NamedValue_ptr nv;
  CORBA_Any temp_value (_tc_CORBA_Long);
  CORBA_Long value;

  req.orb ()->create_list (0, nvlist);
  nv = nvlist->add_value (0, temp_value, CORBA_ARG_IN, env);

  req.params (nvlist, env);
  if (env.exception () != 0)
    {
      dexc (env, "test_throw, get params");
      return;
    }

  value = *(CORBA_Long *)nv->value ()->value ();
  if (env.exception () != 0)
    {
      dexc (env, "test_throw, param value");
      return;
    }

  CORBA_Any_ptr any;

  if (value <= 0) 
    {
      test1_x1		*x;

      x = new test1_x1 (value);
      any = new CORBA_Any (_tc_test1_x1, x, CORBA_B_TRUE);

    }
  else if (value & 0x01)
    {
      test1_x2		*x;

      x = new test1_x2 (CORBA_Object::_nil (), value);
      any = new CORBA_Any (_tc_test1_x2, x, CORBA_B_TRUE);

    } 
  else
    {
#if	0
      test1_x2		*x;

      x = new test1_x2 (req.oa()->target (), value);
      any = new CORBA_Any (_tc_test1_x2, x, CORBA_B_TRUE);
#else
      //
      // XXX right now, we don't have a target() operation on the
      // BOA ... needs to be added.  Verify the client side memory
      // leak of pointers embedded in user exceptions is fixed, too.
      //
      env.exception (new CORBA_IMP_LIMIT (COMPLETED_NO));
      return;
#endif
    }

  req.exception (USER_EXCEPTION, any, env);
}


//
// This table is used to associate operation names with the Dynamic
// Skeleton method ... someday it could return static skeletons.
//
#define	DECL_SKEL(name) \
	{ & test1_ ## name ## _calldata, _test1_test_ ## name }

static const TAO_Skel_Entry test1_operations [] = {
    DECL_SKEL (void),

    DECL_SKEL (octet),
    DECL_SKEL (char),

    DECL_SKEL (wchar),

    DECL_SKEL (short),
    DECL_SKEL (ushort),
    DECL_SKEL (long),
    DECL_SKEL (ulong),

#if	!defined (NONNATIVE_LONGLONG)
    // don't try this on platforms that don't support
    // math on longlongs ...
    DECL_SKEL (longlong),
    DECL_SKEL (ulonglong),
#endif

    DECL_SKEL (float),
    DECL_SKEL (double),

#if	!defined (NONNATIVE_LONGDOUBLE)
	// don't try this on platforms that don't support
	// math on long doubles ...
    DECL_SKEL (longdouble),
#endif	// !NONNATIVE_LONGDOUBLE

    DECL_SKEL (boolean),

    DECL_SKEL (Object),
    DECL_SKEL (TypeCode),

    DECL_SKEL (string),
    DECL_SKEL (wstring),

    { & test1_test_throw_calldata, _test1_test_throw },

    { 0, 0 }
};


//
// Dispatch to method code ...
//
// Knows how to interpret "context" to get target objref, and where to
// get second level skeletons for that target.  Both of those operations
// will generally be abstracted (into library and stub code) so the main
// body of this routine would be invisible to most applications.
//
// However, there are applications that need to do this stuff themselves
// (like bridging between environments, e.g. different ORBs, languages, or
// other object systems).  Everything needed to work without using an IDL
// compiler is a public, supported API.
//
static void
level1_skeleton (
    CORBA_OctetSeq		&key,
    CORBA_ServerRequest		&req,
    void			*context,
    CORBA_Environment		&env
)
{
  //
  // Verify that the target object and "this" object have the
  // same key.  Normally, this would be used to figure out
  // which object was the target, and hence which operations
  // vector to dispatch the request.
  //
  CORBA_OctetSeq		*obj_key;

  obj_key = (CORBA_OctetSeq *) context;
  if (obj_key->length != key.length
      || ACE_OS::memcmp (obj_key->buffer, key.buffer,
			 obj_key->length) != 0)
    {
    env.exception (new CORBA_OBJECT_NOT_EXIST (COMPLETED_NO));
#ifdef	DEBUG
    if (TAO_debug_level)
      dmsg_opaque ("request to nonexistent object, key = ",
		   key.buffer, key.length);
#endif
    return;
  }

  //
  // Find a "level 2 skeleton" for this operation, then
  // call it with the right per-object state.
  //
  const TAO_Skel_Entry	*entry;
  CORBA_String		opname;

  opname = req.op_name ();
  for (entry = &test1_operations [0]; entry->op_descriptor; entry++)
    {
    if (strcmp ((char *)opname, entry->op_descriptor->opname) == 0)
      {
      // We pass a bogus object because this version of the cubit example is
      // so tightly coupled that it doesn't use it.  Future modifications will,
      // hopefully!
      CORBA_Object_ptr bogus_object = 0;
      entry->impl_skeleton (req, bogus_object, env);
      return;
    }
  }

  //
  // XXX True top-level skeleton code would also have to understand
  // the built-in operations:
  //
  // * _is_a (for narrowing tests) ... can be derived by searching an
  //	 appropriately structured graph of level2 skeletons.
  //
  // * _non_existent ... if the level1 skeleton consults a module
  //	 which understands object lifespans, this should be simple.
  // 
  // * _get_interface ... could either (a) fail; (b) return the ref
  //   from some IFR; or most interestingly (c) return a ref to some
  //	 code in this process that can answer all IFR queries from the
  //	 skeleton data structures.
  //
  // * _get_implementation  ... return some administrative hook to
  //   the object implementation:
  //
  // No other legal operations start with a character that's not an
  // ASCII alphanumeric, for what it's worth.
  //
  // The skeleton might want to use data in the object key to find
  // the objref's type; if it's integrated with object creation, and
  // an per-process implementation repository, this should be easy.
  //

  //
  // bypass level 2 skeletons for this one ...
  //
  if (strcmp ((char *) opname, "please_exit") == 0)
    {
    dmsg ("I've been asked to shut down...");
    req.oa ()->please_shutdown (env);
    dexc (env, "please_exit, please_shutdown");
    return;
  }

  //
  // No match.  Operation not implemented; say so.
  //
  dmsg1 ("unknown operation, %s", opname);
  env.exception (new CORBA_BAD_OPERATION (COMPLETED_NO));
}


//
// Create and print the objref, listen for calls on it until done.
//
extern void
print_exception (const CORBA_Exception *, const char *, FILE *f=stdout);

int
OA_listen (CORBA_ORB_ptr orb_ptr,
	   CORBA_BOA_ptr oa_ptr,
	   CORBA_String key,
	   int idle)
{
  //
  // Create the object we'll be implementing.
  //
  CORBA_OctetSeq	obj_key;
  CORBA_Object_ptr	obj;
  CORBA_Environment	env;

  obj_key.buffer = (CORBA_Octet *) key;
  obj_key.length = obj_key.maximum = ACE_OS::strlen (key);

  obj = oa_ptr->create (obj_key, (CORBA_String) "", env);
  if (env.exception () != 0)
    {
      print_exception (env.exception (), "BOA::create");
      return 1;
    }

  //
  // Stringify the objref we'll be implementing, and
  // print it to stdout.  Someone will take that string
  // and give it to some client.
  //
  CORBA_String	str;

  str = orb_ptr->object_to_string (obj, env);
  if (env.exception () != 0)
    {
      print_exception (env.exception (), "object2string");
      return 1;
    }
  ACE_OS::puts ((char *)str);
  ACE_OS::fflush (stdout);
  dmsg1 ("listening as object '%s'", str);

  //
  // Clean up -- "key" is sufficient to dispatch all requests.
  //
  CORBA_release (obj);
  CORBA_string_free (str);
  env.clear ();

  //
  // Handle requests for this object until we're killed, or one of
  // the methods makes us exit.
  //
  // NOTE:  apart from registering the top level skeleton, the rest
  // of this loop is exactly what BOA::run() does.  It's here to
  // show there's no magic.
  //
#if 0
  oa_ptr->register_dir (level1_skeleton, &obj_key, env);
#else
  oa_ptr->register_dir (oa_ptr->dispatch, &obj_key, env);
#endif
  if (env.exception () != 0)
    {
      print_exception (env.exception (), "register_dir");
      return 1;
    }

  for (;;) {
    if (idle == -1)
      oa_ptr->get_request (CORBA_B_FALSE, 0, env);
    else
      {
	timeval		tv;

	tv.tv_sec = idle;
	tv.tv_usec = 0;
	oa_ptr->get_request (CORBA_B_FALSE, &tv, env);
      }

    CORBA_Exception_ptr	xp;

    if ((xp = env.exception ()) != 0)
      {
	CORBA_Environment env2;	// XXX
	char *id;

	id = env.exception ()->id ();

	//
	// We get BAD_INV_ORDER if we call get_request() after
	// shutdown was initiated.  Simpler to rely on that
	// than to arrange any handshaking in this simple app.
	//
	if (strcmp (id, _tc_CORBA_BAD_INV_ORDER->id (env2)) == 0)
	  {
	    break;

	    //
	    // Other exceptions are errors.
	    //
	  }
	else
	  {
	    print_exception (env.exception (), "BOA::get_request");
	    return 1;
	  }
      }
    env.clear ();
  }

  //
  // Shut down the OA -- recycles all underlying resources (e.g. file
  // descriptors, etc).
  //
  // XXX shutdown is not quite the same as release, unless we want mem
  // leaks to cause some rude failure modes.  BOA just hasn't been
  // updated yet to have any handshake about this though.
  //
  oa_ptr->Release ();
  return 0;
}


//
// Standard command line parsing utilities used.
//
int
main (int    argc, char   *argv[])
{
  CORBA_Environment	env;
  CORBA_ORB_ptr	orb_ptr;
  CORBA_BOA_ptr		oa_ptr;
  CORBA_String	key = (CORBA_String) "elvis";
  char		*oa_name = 0;
  char		*orb_name = "internet";
  int			idle = -1;

  //
  // Parse the command line, get options
  //
  ACE_Get_Opt get_opt (argc, argv, "dln:O:x");
  int			c;

  while ((c = get_opt ()) != -1)
    switch (c)
      {
      case 'd':			// more debug noise
	TAO_debug_level++;
	continue;

      case 'i':			// idle seconds b4 exit
	idle = ACE_OS::atoi (get_opt.optarg);
	continue;

      case 'k':			// key (str)
	key = (CORBA_String) get_opt.optarg;
	continue;

      case 'o':			// orb name
	orb_name = get_opt.optarg;
	continue;

      case 'p':			// portnum
	oa_name = get_opt.optarg;
	continue;

	// XXX set debug filters ...

	//
	// XXX ignore OMG-specified options ... hope nobody ever tries
	// to use that "-ORB* param" and "-OA* param" syntax, it flies
	// in the face of standard command parsing algorithms which
	// require single-character option specifiers.
	//


      case '?':
      default:
	ACE_OS::fprintf (stderr, "usage:  %s"
			 " [-d]"
			 " [-i idle_seconds]"
			 " [-k object_key=elvis]"
			 " [-o orbname=internet]"
			 " [-p oa_name]"
			 "\n", argv [0]
			 );
	return 1;
      }

  orb_ptr = CORBA_ORB_init (argc, argv, orb_name, env);
  if (env.exception () != 0) {
    print_exception (env.exception (), "ORB init");
    return 1;
  }

  //
  // The BOA may or may not actually be named ...
  //
  oa_ptr = CORBA_BOA::get_named_boa (orb_ptr, oa_name, env);
  if (env.exception () != 0) {
    print_exception (env.exception (), "OA init");
    return 1;
  }

  return OA_listen (orb_ptr, oa_ptr, key, idle);
}