00001
00002
00003 #include "orbsvcs/PortableGroup/PG_Object_Group.h"
00004 #include "orbsvcs/PortableGroup/PG_conf.h"
00005
00006 #include "orbsvcs/PortableGroup/PG_Operators.h"
00007 #include "orbsvcs/PortableGroup/PG_Utils.h"
00008
00009 #include "tao/debug.h"
00010
00011 #include "ace/Get_Opt.h"
00012 #include "ace/Vector_T.h"
00013
00014
00015
00016
00017
00018
00019
00020
00021 #if defined (__BORLANDC__) && (__BORLANDC__ <= 0x582)
00022 # pragma option push -w-csu
00023 # pragma nopushoptwarn
00024 # pragma nopackwarning
00025 #endif
00026
00027 #define TODO int todo;
00028
00029 TAO_BEGIN_VERSIONED_NAMESPACE_DECL
00030
00031 TAO::PG_Object_Group::MemberInfo::MemberInfo (
00032 CORBA::Object_ptr member,
00033 const PortableGroup::Location & location)
00034 : member_ (CORBA::Object::_duplicate (member))
00035 , factory_(PortableGroup::GenericFactory::_nil ())
00036 , location_ (location)
00037 , is_primary_ (0)
00038 {
00039 }
00040
00041 TAO::PG_Object_Group::MemberInfo::MemberInfo (
00042 CORBA::Object_ptr member,
00043 const PortableGroup::Location & location,
00044 PortableGroup::GenericFactory_ptr factory,
00045 PortableGroup::GenericFactory::FactoryCreationId factory_id)
00046 : member_ (CORBA::Object::_duplicate (member))
00047 , factory_ (PortableGroup::GenericFactory::_duplicate (factory))
00048 , factory_id_ (factory_id)
00049 , location_ (location)
00050 , is_primary_ (0)
00051 {
00052 }
00053
00054 TAO::PG_Object_Group::MemberInfo::~MemberInfo (void)
00055 {
00056 if( ! CORBA::is_nil (this->factory_.in()))
00057 {
00058 this->factory_->delete_object (this->factory_id_);
00059 }
00060 }
00061
00062
00063 TAO::PG_Object_Group::PG_Object_Group (
00064 CORBA::ORB_ptr orb,
00065 PortableGroup::FactoryRegistry_ptr factory_registry,
00066 TAO::PG_Object_Group_Manipulator & manipulator,
00067 CORBA::Object_ptr empty_group,
00068 const PortableGroup::TagGroupTaggedComponent & tagged_component,
00069 const char * type_id,
00070 const PortableGroup::Criteria & the_criteria,
00071 TAO::PG_Property_Set * type_properties)
00072 : internals_()
00073 , orb_ (CORBA::ORB::_duplicate (orb))
00074 , factory_registry_ (PortableGroup::FactoryRegistry::_duplicate (factory_registry))
00075 , manipulator_ (manipulator)
00076 , empty_ (1)
00077 , role_ (type_id)
00078 , type_id_ (CORBA::string_dup (type_id))
00079 , tagged_component_ (tagged_component)
00080 , reference_ (CORBA::Object::_duplicate(empty_group))
00081 , members_ ()
00082 , primary_location_(0)
00083 , properties_ (the_criteria, type_properties)
00084 , initial_number_members_ (0)
00085 , minimum_number_members_ (0)
00086 , group_specific_factories_ ()
00087 {
00088 }
00089
00090 TAO::PG_Object_Group::~PG_Object_Group (void)
00091 {
00092 for (MemberMap_Iterator it = this->members_.begin();
00093 it != this->members_.end();
00094 ++it)
00095 {
00096 MemberInfo * member = (*it).int_id_;
00097 delete member;
00098 }
00099 this->members_.unbind_all ();
00100 }
00101
00102 #if 0 // may want this again someday
00103
00104 // q&d debug function
00105 static void
00106 dump_ior (const char * base,
00107 const char * ext,
00108 unsigned long version,
00109 const char * iogr)
00110 {
00111 char filename[1000];
00112 ACE_OS::sprintf(filename, "%s_%lu.%s", base, version, ext );
00113
00114 FILE * iorfile = ACE_OS::fopen(filename, "w");
00115 ACE_OS::fwrite (iogr, 1, ACE_OS::strlen(iogr), iorfile);
00116 ACE_OS::fclose (iorfile);
00117 }
00118 #endif // may want this again someday
00119
00120 PortableGroup::ObjectGroup_ptr
00121 TAO::PG_Object_Group::reference (void) const
00122 {
00123 ACE_GUARD_RETURN (TAO_SYNCH_MUTEX,
00124 guard,
00125 this->internals_,
00126 PortableGroup::ObjectGroup::_nil ());
00127 return PortableGroup::ObjectGroup::_duplicate (this->reference_);
00128 }
00129
00130 void
00131 TAO::PG_Object_Group::get_group_specific_factories (
00132 PortableGroup::FactoryInfos & result) const
00133 {
00134 ACE_GUARD (TAO_SYNCH_MUTEX, guard, this->internals_);
00135
00136
00137
00138 result = this->group_specific_factories_;
00139 }
00140
00141 const PortableGroup::Location &
00142 TAO::PG_Object_Group::get_primary_location (void) const
00143 {
00144 ACE_GUARD_RETURN (TAO_SYNCH_MUTEX,
00145 guard,
00146 this->internals_,
00147 this->primary_location_);
00148 return this->primary_location_;
00149 }
00150
00151
00152 PortableGroup::ObjectGroup_ptr
00153 TAO::PG_Object_Group::add_member_to_iogr (CORBA::Object_ptr member)
00154 {
00155
00156
00157 PortableGroup::ObjectGroup_var result;
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169 PortableGroup::ObjectGroup_var cleaned =
00170 PortableGroup::ObjectGroup::_duplicate (this->reference_.in ());
00171 if (this->empty_)
00172 {
00173
00174
00175 cleaned =
00176 this->manipulator_.remove_profiles (cleaned.in (),
00177 this->reference_.in ());
00178 this->empty_ = 0;
00179 }
00180
00181
00182 TAO_IOP::TAO_IOR_Manipulation::IORList iors (2);
00183 iors.length (2);
00184 iors [0] = CORBA::Object::_duplicate (cleaned.in());
00185 iors [1] = CORBA::Object::_duplicate (member);
00186
00187
00188 result =
00189 this->manipulator_.merge_iors (iors);
00190 return result._retn ();
00191 }
00192
00193 void
00194 TAO::PG_Object_Group::add_member (const PortableGroup::Location & the_location,
00195 CORBA::Object_ptr member)
00196
00197 {
00198 ACE_GUARD (TAO_SYNCH_MUTEX, guard, this->internals_);
00199
00200
00201
00202
00203
00204
00205
00206
00207 CORBA::String_var member_ior_string =
00208 orb_->object_to_string (member);
00209
00210 PortableGroup::ObjectGroup_var new_reference =
00211 add_member_to_iogr (member);
00212
00213
00214 CORBA::Object_var member_ior =
00215 this->orb_->string_to_object (member_ior_string.in ());
00216
00217 MemberInfo * info = 0;
00218 ACE_NEW_THROW_EX (info,
00219 MemberInfo (member_ior.in (),
00220 the_location),
00221 CORBA::NO_MEMORY());
00222
00223 if (this->members_.bind (the_location, info) != 0)
00224 {
00225
00226 throw CORBA::NO_MEMORY();
00227 }
00228
00229 this->reference_ = new_reference;
00230
00231 if (this->increment_version ())
00232 {
00233 this->distribute_iogr ();
00234 }
00235 else
00236 {
00237 throw PortableGroup::ObjectNotAdded ();
00238 }
00239
00240 if (TAO_debug_level > 6)
00241 {
00242 ACE_DEBUG ((LM_DEBUG,
00243 ACE_TEXT("PG (%P|%t) exit Object_Group add_member \n")));
00244 }
00245 }
00246
00247 int
00248 TAO::PG_Object_Group::set_primary_member (
00249 TAO_IOP::TAO_IOR_Property * prop,
00250 const PortableGroup::Location & the_location)
00251 {
00252 ACE_GUARD_RETURN (TAO_SYNCH_MUTEX,
00253 guard,
00254 this->internals_,
00255 0);
00256 int result = 1;
00257 MemberInfo * info = 0;
00258 if (this->members_.find (the_location, info) == 0)
00259 {
00260 int cleared = 0;
00261 this->primary_location_ = the_location;
00262 for (MemberMap_Iterator it = this->members_.begin();
00263 !cleared && it != this->members_.end();
00264 ++it)
00265 {
00266 cleared = (*it).int_id_->is_primary_;
00267 (*it).int_id_->is_primary_ = 0;
00268 }
00269 info->is_primary_ = 1;
00270
00271 int set_ok =
00272 this->manipulator_.set_primary (prop,
00273 this->reference_.in (),
00274 info->member_.in ());
00275 if (!set_ok)
00276 {
00277 if (TAO_debug_level > 3)
00278 {
00279 ACE_ERROR ((LM_ERROR,
00280 ACE_TEXT ("%T %n (%P|%t) - ")
00281 ACE_TEXT ("Can't set primary in IOGR .\n")
00282 ));
00283 }
00284
00285 result = 0;
00286 }
00287
00288 if (result && this->increment_version ())
00289 {
00290 this->distribute_iogr ();
00291 }
00292 else
00293 {
00294 if (TAO_debug_level > 3)
00295 {
00296 ACE_DEBUG ((LM_DEBUG,
00297 ACE_TEXT("TAO-PG (%P|%t) - set_primary_location ")
00298 ACE_TEXT("throwing PrimaryNotSet because increment")
00299 ACE_TEXT("version failed.\n")
00300 ));
00301 }
00302
00303 result = 0;
00304 }
00305 }
00306 else
00307 {
00308 if (TAO_debug_level > 3)
00309 {
00310 ACE_DEBUG ((LM_DEBUG,
00311 ACE_TEXT ("TAO-PG (%P|%t) - set_primary_location ")
00312 ACE_TEXT ("throwing MemberNotFound.\n")));
00313 }
00314 throw PortableGroup::MemberNotFound();
00315 }
00316
00317 return result;
00318 }
00319
00320
00321 void
00322 TAO::PG_Object_Group::remove_member (
00323 const PortableGroup::Location & the_location)
00324 {
00325 ACE_GUARD (TAO_SYNCH_MUTEX, guard, this->internals_);
00326 MemberInfo * info = 0;
00327 if (this->members_.unbind (the_location, info) == 0)
00328 {
00329 if (this->members_.current_size() > 0)
00330 {
00331 this->reference_ =
00332 this->manipulator_.remove_profiles (this->reference_.in (),
00333 info->member_.in ());
00334 }
00335 else
00336 {
00337 empty_ = 1;
00338 }
00339
00340 delete info;
00341
00342 if (the_location == this->primary_location_)
00343 {
00344 this->primary_location_.length(0);
00345 }
00346
00347 if (this->increment_version ())
00348 {
00349 this->distribute_iogr ();
00350 }
00351
00352 }
00353 else
00354 {
00355 if (TAO_debug_level > 6)
00356 {
00357 ACE_DEBUG ((LM_DEBUG,
00358 "TAO-PG (%P|%t) - "
00359 "remove_member throwing MemberNotFound.\n"
00360 ));
00361 }
00362 throw PortableGroup::MemberNotFound();
00363 }
00364 }
00365
00366
00367 PortableGroup::ObjectGroupId
00368 TAO::PG_Object_Group::get_object_group_id (void) const
00369 {
00370 ACE_GUARD_RETURN (TAO_SYNCH_MUTEX,
00371 guard,
00372 this->internals_,
00373 0);
00374 return this->tagged_component_.object_group_id;
00375 }
00376
00377 void
00378 TAO::PG_Object_Group::set_properties_dynamically (
00379 const PortableGroup::Properties & overrides)
00380 {
00381 ACE_GUARD (TAO_SYNCH_MUTEX, guard, this->internals_);
00382
00383 this->properties_.decode (overrides);
00384
00385
00386 }
00387
00388 void
00389 TAO::PG_Object_Group::get_properties (
00390 PortableGroup::Properties_var & result) const
00391 {
00392 ACE_GUARD (TAO_SYNCH_MUTEX, guard, this->internals_);
00393 this->properties_.export_properties(*result);
00394 }
00395
00396
00397 PortableGroup::TypeId
00398 TAO::PG_Object_Group::get_type_id (void) const
00399 {
00400 ACE_GUARD_RETURN (TAO_SYNCH_MUTEX,
00401 guard,
00402 this->internals_,
00403 0);
00404 return CORBA::string_dup (this->type_id_);
00405 }
00406
00407
00408
00409
00410
00411 int
00412 TAO::PG_Object_Group::increment_version (void)
00413 {
00414
00415 int result = 0;
00416 this->tagged_component_.object_group_ref_version += 1;
00417 if (TAO_debug_level > 3)
00418 {
00419 ACE_DEBUG ((LM_DEBUG,
00420 ACE_TEXT ("%T %n (%P|%t) - Setting IOGR version to %u\n"),
00421 static_cast<unsigned> (this->tagged_component_.object_group_ref_version)
00422 ));
00423 }
00424
00425
00426 if (TAO::PG_Utils::set_tagged_component (this->reference_,
00427 this->tagged_component_))
00428 {
00429 result = 1;
00430 }
00431 return result;
00432 }
00433
00434
00435
00436
00437 void
00438 TAO::PG_Object_Group::distribute_iogr (void)
00439 {
00440
00441 CORBA::String_var iogr =
00442 this->orb_->object_to_string (this->reference_.in());
00443
00444
00445 for (MemberMap_Iterator it = this->members_.begin();
00446 it != this->members_.end ();
00447 ++it)
00448 {
00449 MemberInfo const * info = (*it).int_id_;
00450
00451
00452
00453
00454
00455
00456
00457 PortableGroup::TAO_UpdateObjectGroup_var uog =
00458 PortableGroup::TAO_UpdateObjectGroup::_narrow ( info->member_.in ());
00459 if (!CORBA::is_nil (uog.in ()))
00460 {
00461 try
00462 {
00463 if (TAO_debug_level > 3)
00464 {
00465 ACE_DEBUG ((LM_DEBUG,
00466 "PG (%P|%t) - Object_Group pushing "
00467 "IOGR to %s member: %s@%s.\n",
00468 (info->is_primary_ ? "Primary" : "Backup"),
00469 this->role_.c_str (),
00470 static_cast<const char *> (info->location_[0].id)
00471 ));
00472 }
00473
00474
00475
00476 uog->tao_update_object_group (iogr.in (),
00477 this->tagged_component_.object_group_ref_version,
00478 info->is_primary_);
00479 }
00480 catch (const CORBA::Exception&)
00481 {
00482
00483
00484 }
00485 }
00486 else
00487 {
00488 ACE_ERROR ((LM_ERROR,
00489 "TAO::PG_Object_Group::distribute iogr can't "
00490 "narrow member reference to "
00491 "PortableGroup::TAO_UpdateObjectGroup.\n"
00492 ));
00493 }
00494 }
00495 }
00496
00497 PortableGroup::Locations *
00498 TAO::PG_Object_Group::locations_of_members (void)
00499 {
00500 ACE_GUARD_RETURN (TAO_SYNCH_MUTEX,
00501 guard,
00502 this->internals_,
00503 0);
00504
00505 PortableGroup::Locations * result = 0;
00506
00507 size_t count = this->members_.current_size ();
00508
00509 ACE_NEW_THROW_EX (
00510 result,
00511 PortableGroup::Locations (count),
00512 CORBA::NO_MEMORY() );
00513
00514 result->length (count);
00515
00516 size_t pos = 0;
00517 for (MemberMap_Iterator it = this->members_.begin();
00518 it != this->members_.end();
00519 ++it)
00520 {
00521 const PortableGroup::Location & location = (*it).ext_id_;
00522 PortableGroup::Location & out = (*result)[pos];
00523 out = location;
00524 }
00525 return result;
00526 }
00527
00528 CORBA::Object_ptr
00529 TAO::PG_Object_Group::get_member_reference (
00530 const PortableGroup::Location & the_location)
00531 {
00532 ACE_GUARD_RETURN (TAO_SYNCH_MUTEX,
00533 guard,
00534 this->internals_,
00535 CORBA::Object::_nil ());
00536
00537 CORBA::Object_var result;
00538
00539 MemberInfo * info = 0;
00540 if (this->members_.find (the_location, info) == 0)
00541 {
00542 result = CORBA::Object::_duplicate (info->member_.in ());
00543 }
00544 else
00545 {
00546 throw PortableGroup::MemberNotFound();
00547 }
00548 return result._retn ();
00549 }
00550
00551
00552 PortableGroup::MembershipStyleValue
00553 TAO::PG_Object_Group::get_membership_style (void) const
00554 {
00555 PortableGroup::MembershipStyleValue membership_style = 0;
00556 if (!TAO::find (properties_,
00557 PortableGroup::PG_MEMBERSHIP_STYLE,
00558 membership_style))
00559 {
00560 membership_style = TAO_PG_MEMBERSHIP_STYLE;
00561 }
00562 return membership_style;
00563 }
00564
00565
00566 PortableGroup::MinimumNumberMembersValue
00567 TAO::PG_Object_Group::get_minimum_number_members (void) const
00568 {
00569 PortableGroup::MinimumNumberMembersValue minimum_number_members = 0;
00570 if (!TAO::find (properties_,
00571 PortableGroup::PG_MINIMUM_NUMBER_MEMBERS,
00572 minimum_number_members))
00573 {
00574 minimum_number_members = TAO_PG_MINIMUM_NUMBER_MEMBERS;
00575 }
00576 return minimum_number_members;
00577 }
00578
00579 PortableGroup::InitialNumberMembersValue
00580 TAO::PG_Object_Group::get_initial_number_members (void) const
00581 {
00582 PortableGroup::InitialNumberMembersValue initial_number_members = 0;
00583 if (!TAO::find (properties_,
00584 PortableGroup::PG_INITIAL_NUMBER_MEMBERS,
00585 initial_number_members))
00586 {
00587 initial_number_members = TAO_PG_INITIAL_NUMBER_MEMBERS;
00588 }
00589 return initial_number_members;
00590 }
00591
00592 void
00593 TAO::PG_Object_Group::create_member (
00594 const PortableGroup::Location & the_location,
00595 const char * type_id,
00596 const PortableGroup::Criteria & the_criteria)
00597 {
00598 ACE_GUARD (TAO_SYNCH_MUTEX, guard, this->internals_);
00599
00600
00601 if (0 != this->members_.find (the_location))
00602 {
00603
00604
00605 CORBA::String_var factory_type;
00606 PortableGroup::FactoryInfos_var factories =
00607 this->factory_registry_->list_factories_by_role (
00608 role_.c_str(),
00609 factory_type.out ());
00610
00611
00612
00613 int created = 0;
00614 CORBA::ULong factory_count = factories->length ();
00615 for (CORBA::ULong factory_pos = 0;
00616 ! created && factory_pos < factory_count;
00617 ++factory_pos)
00618 {
00619 const PortableGroup::FactoryInfo & factory_info =
00620 (*factories)[factory_pos];
00621 if (factory_info.the_location == the_location)
00622 {
00623
00624
00625
00626 PortableGroup::GenericFactory::FactoryCreationId_var fcid;
00627 CORBA::Object_var member =
00628 factory_info.the_factory->create_object (
00629 type_id,
00630 the_criteria,
00631 fcid. out());
00632
00633
00634
00635 CORBA::String_var member_ior_string =
00636 orb_->object_to_string (member.in ());
00637
00638 PortableGroup::ObjectGroup_var new_reference =
00639 this->add_member_to_iogr (member.in ());
00640
00641
00642 CORBA::Object_var member_ior =
00643 this->orb_->string_to_object (member_ior_string.in ());
00644
00645 MemberInfo * info = 0;
00646 ACE_NEW_THROW_EX (info, MemberInfo(
00647 member_ior.in(),
00648 the_location,
00649 factory_info.the_factory,
00650 fcid.in ()),
00651 CORBA::NO_MEMORY());
00652
00653 if (this->members_.bind (the_location, info) != 0)
00654 {
00655 throw CORBA::NO_MEMORY();
00656 }
00657
00658 this->reference_ = new_reference;
00659
00660
00661 if (this->increment_version ())
00662 {
00663 this->distribute_iogr ();
00664 }
00665 created = 1;
00666 }
00667 }
00668 if (! created)
00669 {
00670 throw PortableGroup::NoFactory ();
00671 }
00672 }
00673 else
00674 {
00675 throw PortableGroup::MemberAlreadyPresent ();
00676 }
00677 }
00678
00679 void
00680 TAO::PG_Object_Group::create_members (size_t count)
00681 {
00682
00683
00684
00685 CORBA::String_var factory_type;
00686 PortableGroup::FactoryInfos_var factories =
00687 this->factory_registry_->list_factories_by_role (
00688 role_.c_str(),
00689 factory_type.out ());
00690
00691 CORBA::ULong factory_count = factories->length ();
00692 if (factory_count > 0)
00693 {
00694 CORBA::ULong factory_pos = 0;
00695 while (members_.current_size () < count && factory_pos < factory_count)
00696 {
00697 const PortableGroup::FactoryInfo & factory_info =
00698 (*factories)[factory_pos];
00699 const PortableGroup::Location & factory_location =
00700 factory_info.the_location;
00701 if (0 != this->members_.find (factory_location))
00702 {
00703
00704
00705
00706 try
00707 {
00708 PortableGroup::GenericFactory::FactoryCreationId_var fcid;
00709 CORBA::Object_var member =
00710 factory_info.the_factory->create_object (
00711 this->type_id_.in (),
00712 factory_info.the_criteria,
00713 fcid. out());
00714
00715
00716
00717 CORBA::String_var member_ior_string =
00718 orb_->object_to_string (member.in ());
00719
00720 PortableGroup::ObjectGroup_var new_reference =
00721 this->add_member_to_iogr (member.in ());
00722
00723
00724 CORBA::Object_var member_ior =
00725 this->orb_->string_to_object (member_ior_string.in ());
00726
00727 MemberInfo * info = 0;
00728 ACE_NEW_THROW_EX (info, MemberInfo(
00729 member_ior.in(),
00730 factory_location,
00731 factory_info.the_factory,
00732 fcid.in ()),
00733 CORBA::NO_MEMORY());
00734
00735 if (this->members_.bind (factory_location, info) != 0)
00736 {
00737 throw CORBA::NO_MEMORY();
00738 }
00739 this->reference_ =
00740 new_reference;
00741
00742 }
00743 catch (const CORBA::Exception&)
00744 {
00745
00746 if (TAO_debug_level > 0)
00747 {
00748 ACE_ERROR ((LM_ERROR,
00749 ACE_TEXT ("PG (%P|%t) Replica Factory ")
00750 ACE_TEXT ("@ %s refused create_object ")
00751 ACE_TEXT ("request for type %s\n"),
00752 static_cast<const char *> (factory_info.the_location[0].id),
00753 static_cast<const char *> (this->type_id_.in ())
00754 ));
00755 }
00756 }
00757 }
00758 }
00759
00760 if (this->increment_version ())
00761 {
00762 this->distribute_iogr ();
00763 }
00764 }
00765 else
00766 {
00767 throw PortableGroup::NoFactory();
00768 }
00769 }
00770
00771 void
00772 TAO::PG_Object_Group::initial_populate (void)
00773 {
00774 ACE_GUARD (TAO_SYNCH_MUTEX, guard, this->internals_);
00775
00776 if (this->get_membership_style () == PortableGroup::MEMB_INF_CTRL)
00777 {
00778 PortableGroup::InitialNumberMembersValue initial_number_members =
00779 this->get_initial_number_members ();
00780
00781 if (this->members_.current_size () < initial_number_members)
00782 {
00783 this->create_members (initial_number_members);
00784 }
00785 }
00786 }
00787
00788 void
00789 TAO::PG_Object_Group::minimum_populate (void)
00790 {
00791 ACE_GUARD (TAO_SYNCH_MUTEX, guard, this->internals_);
00792
00793 if ( this->get_membership_style () == PortableGroup::MEMB_INF_CTRL )
00794 {
00795 PortableGroup::MinimumNumberMembersValue minimum_number_members =
00796 this->get_minimum_number_members ();
00797 if (members_.current_size () < minimum_number_members)
00798 {
00799 this->create_members (minimum_number_members);
00800 }
00801 }
00802 }
00803
00804 int
00805 TAO::PG_Object_Group::has_member_at (const PortableGroup::Location & location)
00806 {
00807 return (0 == this->members_.find (location));
00808 }
00809
00810 TAO_END_VERSIONED_NAMESPACE_DECL
00811
00812
00813 #if defined (__BORLANDC__) && (__BORLANDC__ <= 0x582)
00814 # pragma option pop
00815 # pragma nopushoptwarn
00816 # pragma nopackwarning
00817 #endif