- Overview
- Names
- The Initialization Service
- Basic Operations
- Server Example
- Client Example
- Federated Naming Service
- Binding Iterators and the list() Operation
- Object URLs
- Specifying Values for Initial References
- Summary
- Initialization Service Pseudo-IDL
- Naming Service IDL
Binding Iterators and the list() Operation
Another basic operation on the naming service is the list() operation, which returns the contents of a naming context. In addition to the simple task of printing out the contents of a naming context, the list() operation is useful for any tasks that require traversal of the naming graph.
The IDL Interface for list() and BindingIterator
At first, it would appear that list() should be a simple operation: It is invoked on a naming context and returns a list of bindings contained by that naming context. But what if the naming context happens to contain one million objects? This might seem a little far-fetched for the particular project you are working on, but it is realistic for some applications. For example, if the naming service is used to wrap a database, it would not be unusual to find millions of objects in a naming context. In order to cope with this situation, the naming service defines a BindingIterator interface that sends over parts of the listing in manageable-size pieces.
The IDL relating to the list() operation is declared as follows:
// IDL module CosNaming { enum BindingType { nobject, ncontext }; struct Binding { Name binding_name; BindingType binding_type; }; typedef sequence <Binding> BindingList; interface NamingContext { ... void list( in unsigned long how_many, out BindingList bl, out BindingIterator bi ); }; interface BindingIterator { boolean next_one(out Binding b); boolean next_n(in unsigned long how_many, out BindingList bl); void destroy(); }; ... };
When the list() operation is invoked on a naming context, it returns BindingList, which holds a sequence of bindings belonging to that naming context. The how_many argument is used to specify the maximum number of bindings that can be returned in BindingList.
Two cases arise, according to whether or not the returned BindingIterator bi is null:
If the BindingIterator bi is a null object reference, the BindingList bl returned by list() is complete.
If the BindingIterator bi is not null, the BindingList bl returned by list() is incomplete. It is then necessary to invoke the BindingIterator object to retrieve the remaining bindings. You can retrieve the remaining values either one by one, using next_one(), or in larger chunks, using next_n(). The operations next_one() and next_n() return true if more bindings remain to be retrieved and false if the end of the list has been reached.
Semantics of list() and BindingIterator
The CORBA specification gives a good deal of latitude to the implementation of list() and BindingIterator. The following points are important:
The argument how_many specifies a maximum for the number of returned bindings. There is no guarantee that you will get all of the bindings with an invocation of list(), even if the number of bindings in the naming context is fewer than how_many. The specification requires a minimum of only one binding to be returned from a nonempty naming context. Therefore, you must always check whether or not a returned BindingIterator is null.
Likewise, in the operation next_n(), the argument how_many specifies a maximum for the number of returned bindings. The number of bindings returned may lie anywhere in the range [1..how_many], irrespective of the number of bindings that remain to be listed.
It is legal to pass a zero value of how_many to list(). This is taken to indicate that you want to use the binding iterator bi to retrieve the list of bindings. In this case, the returned binding list bl is always a zero-length sequence.
It is illegal to pass a zero value of how_many to next_n(); this will give rise to a CORBA::BAD_PARAM exception.
Cleanup After Listing Is Finished
There are a few points to take care of once a listing is finished:
The only way to tell if a listing is finished is by checking the return value of next_one() or next_n(). A value of false indicates that there are no more bindings to retrieve and that the current return value is empty. A further call to next_one() or next_n() after they have returned false has undefined behavior.
The BindingIterator object in the server has to be deleted. The best approach is for the client to explicitly call destroy() on the BindingIterator object. Alternatively, the server may spontaneously delete the BindingIterator (garbage collection) in order to keep its memory usage under control. The client has to be prepared to deal with a CORBA::OBJECT_NOT_EXIST in that case.
The garbage collection of BindingIterator is a typical issue that crops up in a distributed system. In general, for any session object (such as BindingIterator), a server might need to implement garbage collection to clean out those objects that have been lying dormant for a relatively long time.
Name Utility - listBindings()
The combination of list() and BindingIterator to retrieve lists of bindings is flexible but awkward to use. It is easier to use a method that returns the listing in a single invocation. The listBindings() method of our name utility is such a method.
An implementation of listBindings() is shown for C++ and Java in Listing 11 and Listing 12:
Listing 11: C++ Implementation of listBindings()
// C++ //---------------------------------------- // method: 'listBindings()' // // purpose: //---------------------------------------- CosNaming::BindingList * NameUtil::listBindings( const CosNaming::NamingContext_ptr nc, const CosNaming::Name& name, CORBA::ULong max_list_size ) { CosNaming::BindingList_var basicListV; CosNaming::BindingIterator_var bIterV; CORBA::Object_var objV; CosNaming::NamingContext_var tmpContextV; if (name.length()==0) { tmpContextV = CosNaming::NamingContext::_duplicate(nc); } else { objV = nc->resolve(name); tmpContextV = CosNaming::NamingContext::_narrow(objV); } if (CORBA::is_nil(tmpContextV)) { cerr << "listBindings: Nil context" << endl; return 0; } tmpContextV->list(max_list_size, basicListV.out(), bIterV.out()); CORBA::Long max_remaining = max_list_size - basicListV->length(); CORBA::Boolean moreBindings = !CORBA::is_nil(bIterV); if (moreBindings) { while (moreBindings && (max_remaining > 0) ) { CosNaming::BindingList_var tmpListV; moreBindings = bIterV->next_n(max_remaining, tmpListV.out()); //Append 'tmpListV' to 'basicListV' CORBA::ULong basicListLen = basicListV->length(); basicListV->length(basicListLen+tmpListV->length()); for (CORBA::ULong i=0; i < tmpListV->length(); i++) { (*basicListV)[i+basicListLen] = (*tmpListV)[i]; } //Re-calculate 'max_remaining' max_remaining = max_list_size - basicListV->length(); } bIterV->destroy(); } return basicListV._ retn(); }
Listing 12: Java Implementation of listBindings()
// Java //---------------------------------------- // method: 'listBindings()' // // purpose: //---------------------------------------- public static Binding[] listBindings( NamingContext nc, NameComponent[] name, int max_list_size ) throws org.omg.CORBA.UserException { Binding[] basicList; BindingIterator bIter; org.omg.CORBA.Object obj; NamingContext tmpContext; if (name.length==0) { tmpContext = nc; } else { obj = nc.resolve(name); tmpContext = NamingContextHelper.narrow(obj); } BindingListHolder basicList_out = new BindingListHolder(); BindingIteratorHolder bIter_out = new BindingIteratorHolder(); tmpContext.list( max_list_size, basicList_out, bIter_out ); basicList = basicList_out.value; bIter = bIter_out.value; int max_remaining = max_list_size - basicList.length; boolean moreBindings = (bIter!=null); if (moreBindings) { while (moreBindings && (max_remaining > 0) ) { Binding[] tmpList; Binding[] oldList; BindingListHolder tmpList_out = new BindingListHolder(); moreBindings = bIter.next_n( max_list_size, tmpList_out ); tmpList = tmpList_out.value; //Append 'tmpList' to 'basicList' oldList = basicList; basicList = new Binding[oldList.length+tmpList.length]; for (int i=0; i < oldList.length; i++) { basicList[i] = oldList[i]; } for (int i=0; i < tmpList.length; i++) { basicList[i+oldList.length] = tmpList[i]; } // Re-calculate 'max_remaining' max_remaining = max_list_size - basicList.length; } bIter.destroy(); } return basicList; }
The naming context that you want to obtain the listing for is specified by passing the initial naming context nc and the name of the context relative to the initial context. The argument max_list_size is used to specify the largest length of BindingList that you find acceptable. The implementation of listBindings() does not return until either it has retrieved all of the bindings or it reaches the limit max_list_size.
Name Utility - recursiveUnbind()
As an application of the utility function listBindings(), consider the common task of deleting a naming context and all its contents. In order to delete the naming context completely, it is necessary to recurse into all of the subcontexts and delete their contents as well.
With the help of listBindings(), it is straightforward to implement recursiveUnbind(), as shown in Listing 13 and Listing 14 for C++ and Java.
Listing 13: C++ Implementation of recursiveUnbind()
// C++ //---------------------------------------- // method: 'recursiveUnbind()' // // purpose: //---------------------------------------- void NameUtil::recursiveUnbind( const CosNaming::NamingContext_ptr nc, const CosNaming::Name& name ) { CORBA::Object_var objV; CosNaming::NamingContext_var tmpContextV; objV = nc->resolve(name); tmpContextV = CosNaming::NamingContext::_narrow(objV); if (CORBA::is_nil(tmpContextV)) { cerr << "recursiveUnbind: Nil context reference" << endl; return; } CosNaming::BindingList_var blV; CosNaming::Name tmpName; tmpName.length(0); blV = NameUtil::listBindings(tmpContextV.in(), tmpName, 10000 // 'max_list_size' ); for (CORBA::ULong i=0; i<blV->length(); i++) { tmpName = (*blV)[i].binding_name; if ( (*blV)[i].binding_type==CosNaming::nobject) { tmpContextV->unbind(tmpName); } else if ( (*blV)[i].binding_type==CosNaming::ncontext) { NameUtil::recursiveUnbind(tmpContextV.in(), tmpName); } } nc->unbind(name); tmpContextV->destroy(); }
Listing 14: Java Implementation of recursiveUnbind()
// Java //---------------------------------------- // method: 'recursiveUnbind()' // // purpose: //---------------------------------------- public static void recursiveUnbind( NamingContext nc, NameComponent[] name ) throws org.omg.CORBA.UserException { org.omg.CORBA.Object obj; NamingContext tmpContext; obj = nc.resolve(name); tmpContext = NamingContextHelper.narrow(obj); Binding[] bl; NameComponent[] tmpName = new NameComponent[0]; bl = NameUtil.listBindings(tmpContext, tmpName, 10000 // max_list_size ); for (int i=0; i < bl.length; i++) { tmpName = bl[i].binding_name; if ( bl[i].binding_type==BindingType.nobject) { tmpContext.unbind(tmpName); } else if ( bl[i].binding_type==BindingType.ncontext) { NameUtil.recursiveUnbind(tmpContext, tmpName); } } nc.unbind(name); tmpContext.destroy(); }
The naming context that you want to delete is specified by passing the initial naming context nc and the name of the context relative to the initial context. Note there are a couple of limitations of recursiveUnbind() that make it less than industrial-strength. The maximum list size cannot be specified, nor does it deal with a situation in which the maximum list size is exceeded. Also, this method makes the assumption that the naming service is arranged strictly in the form of a tree. It cannot deal with naming graphs containing cycles.