Date: Fri, 29 Mar 2024 11:37:53 +0000 (UTC) Message-ID: <954715859.25399.1711712273993@ae5f4610bf64> Subject: Exported From Confluence MIME-Version: 1.0 Content-Type: multipart/related; boundary="----=_Part_25398_559292037.1711712273993" ------=_Part_25398_559292037.1711712273993 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Content-Location: file:///C:/exported.html
On this page:
There are two types of indirect p= roxies: one specifies an identity and an object adapter identifier, whi= le the other contains only an identity. The latter type of indirect proxy i= s known as a well-known proxy. A well-known proxy refers to a well= -known object, that is, its identity alone is sufficient to allow the clien= t to locate it. Ice requires all object identities in an application to be = unique, but typically only a select few objects are able to be located only= by their identities.
In earlier sections we showed the relationship between indirect proxies =
containing an object adapter identifier and the IceGrid configuration. Brie=
fly, in order for a client to use a proxy such as factory@EncoderAdap=
ter
, an object adapter must be given the identifier EncoderAda=
pter
.
A similar requirement exists for well-known objects. The registry mainta= ins a table of these objects, which can be populated in a number of ways:= p>
The registry's database maps an object identity to a proxy. A locate req= uest containing only an identity prompts the registry to consult this datab= ase. If a match is found, the registry examines the associated proxy to det= ermine if additional work is necessary. For example, consider the well-know= n objects in the following table.
Identity |
Proxy |
---|---|
|
|
|
|
|
|
The proxy associated with Object1
already contains endpoint=
s, so the registry can simply return this proxy to the client.
For Object2
, the registry notices the adapter ID and checks=
to see whether it knows about an adapter identified as TheAdapter. If it does, it attempts to obtain the endpoints of that adapter, whic=
h may cause its server to be started. If the registry is successfully able =
to determine the adapter's endpoints, it returns a direct proxy containing =
those endpoints to the client. If the registry does not recognize
The=
Adapter
or cannot obtain its endpoints, it returns the indirect prox=
y Object2@TheAdapter
to the client. Upon receipt of another in=
direct proxy, the Ice run time in the client will try once more to resolve =
the proxy, but generally this will not succeed and the Ice run time in the =
client will raise a NoEndpointException
as a result.
Finally, Object3
represents a hopeless situation: how can t=
he registry resolve Object3
when its associated proxy refers t=
o itself? In this case, the registry returns the proxy Object3
=
to the client, which causes the client to raise NoEndpointException<=
/code>. Clearly, you should avoid this situation.
The registry's database not only associates an identity with a proxy, bu=
t also a type. Technically, the "type" is an arbitrary string but, by conve=
ntion, that string represents the most-derived Slice type of the object. Fo=
r example, the Slice type ID o=
f the encoder factory in our ripper application is ::Ripper::MP3Encod=
erFactory
.
Object types are useful when performing queries.
The object descript= or adds a well-known object to the registry. It must appear within the = context of an adapter descriptor, as shown in the XML example below:
<icegrid> <application name=3D"Ripper"> <node name=3D"Node1= "> <= ;server id=3D"EncoderServer" exe=3D"/opt/ripper/bin/server" activation= =3D"on-demand"> &nb= sp; <adapter name=3D"EncoderAdapter" id=3D"Encoder= Adapter" endpoints=3D"tcp"> &nb= sp; <object identity=3D"En= coderFactory" type=3D"::Ripper::MP3EncoderFactory"/> &nb= sp; </adapter> <= ;/server> </node> </application> </icegrid>
During deployment, the registry associates the identity EncoderFac=
tory
with the indirect proxy EncoderFactory@EncoderAdapter. If the adapter descriptor had omitted the adapter ID, the registry wou=
ld have generated a unique identifier by combining the server ID and the ad=
apter name.
In this example, the object's type is specified explicitly.
The IceGrid::Admin
interface defines several operations tha=
t manipulate the registry's database of well-known objects:
module IceGrid { interface Admin { ... void addObject(Object* obj) throws ObjectExistsExc= eption, &nb= sp; DeploymentException; void updateObject(Object* obj) throws ObjectNotRegist= eredException, &nb= sp; DeploymentException; void addObjectWithType(Object* obj, = string type) throws ObjectExistsExc= eption, &nb= sp; DeploymentException; void removeObject(Ice::Identity id) throws ObjectNotRegist= eredException, &nb= sp; DeploymentException; ... }; };
addObject
addObject
operation adds a new object to the database. Th=
e proxy argument supplies the identity of the well-known object. If an obje=
ct with the same identity has already been registered, the operation raises=
ObjectExistsException
. Since this operation does not accept a=
n argument supplying the object's type, the registry invokes ice_id=
code> on the given proxy to determine its most-derived type. The implicatio=
n here is that the object must be available in order for the registry to ob=
tain its type. If the object is not available, addObject
raise=
s DeploymentException
.
updateObject
updateObject
operation supplies a new proxy for the well-=
known object whose identity is encapsulated by the proxy. If no object with=
the given identity is registered, the operation raises ObjectNotRegi=
steredException
. The object's type is not modified by this operation=
.addObjectWithType
addObjectWithType
operation behaves like addObject<=
/code>, except the object's type is specified explicitly and therefore the =
registry does not attempt to invoke ice_id
on the given proxy =
(even if the type is an empty string).
removeObject
removeObject
operation removes the well-known object with=
the given identity from the database. If no object with the given identity=
is registered, the operation raises ObjectNotRegisteredException.
The following C++ example produces the same result as the descriptor we deployed earli= er:
Ice::ObjectAdapterPtr adapter =3D communi= cator->createObjectAdapter("EncoderAdapter"); Ice::Identity ident =3D communicator->stringToIdentity("Encode= rFactory"); FactoryPtr f=3D new FactoryI; Ice::ObjectPrx factory =3D adapter->add(f, ident); IceGrid::AdminPrx admin =3D // ... try { admin->addObject(factory); // OOPS! } catch (const IceGrid::ObjectExistsException &)&nb= sp;{ admin->updateObject(factory); }
After obtaining a proxy for the IceGrid::Admin
interface, the code invo=
kes addObject
. Notice that the code traps ObjectExistsEx=
ception
and calls updateObject
instead when the object =
is already registered.
There is one subtle problem in this code: calling addObject
=
causes the registry to invoke ice_id
on our factory object, b=
ut we have not yet activated the object adapter. As a result, our program w=
ill hang indefinitely at the call to addObject
. One solution i=
s to activate the adapter prior to the invocation of addObject
=
; another solution is to use addObjectWithType
as shown below:=
Ice::ObjectAdapterPtr adapter =3D communi= cator->createObjectAdapter("EncoderAdapter"); Ice::Identity ident =3D communicator->stringToIdentity("Encode= rFactory"); FactoryPtr f =3D new FactoryI; Ice::ObjectPrx factory =3D adapter->add(f, ident); IceGrid::AdminPrx admin =3D // ... try { admin->addObjectWithType(factory, factory-&= gt;ice_id()); } catch (const IceGrid::ObjectExistsException &)&nb= sp;{ admin->updateObject(factory); }
icegridadmin
The icegridadm=
in utility provides commands that are the functional equivalents of the=
Slice operations for managing well-known objects. We can use the utility to=
manually register the EncoderFactory
object from our descriptors:
$ icegridadmin --Ice.Config=3D/opt/ripper/config >>> object add "EncoderFactory@EncoderAdapter"
Use the object list
command to verify that the object was r=
egistered successfully:
>>> object list EncoderFactory IceGrid/Query IceGrid/Locator IceGrid/Registry IceGrid/InternalRegistry-Master
To specify the object's type explicitly, append it to the object a=
dd
command:
>>> object add "EncoderFactory@EncoderAda= pter" "::Ripper::MP3EncoderFactory"
Finally, the object is removed from the registry like this:
>>> object remove "EncoderFactory"
The registry's database of well-known objects is not used solely for res=
olving indirect proxies. The database can also be queried interactively to =
find objects in a variety of ways. The IceGrid::Query
interfac=
e supplies this functionality:
module IceGrid { enum LoadSample { LoadSample1, LoadSample5, LoadSample15 }; interface Query { idempotent Object* findObjectById(Ice::Id= entity id); idempotent Object* findObjectByType(strin= g type); idempotent Object* findObjectByTypeOnLeas= tLoadedNode(string type, LoadSample sample); idempotent Ice::ObjectProxySeq findAllObj= ectsByType(string type); idempotent Ice::ObjectProxySeq findAllRep= licas(Object* proxy); }; };
findObjectById
findObjectById
operation returns the proxy associated wit=
h the given identity of a well-known object. It returns a null proxy if no =
match was found.findObjectByType
findObjectByType
operation returns a proxy for an object =
registered with the given type. If more than one object has the same type, =
the registry selects one at random. The operation returns a null proxy if n=
o match was found.findObjectByTypeOnLeastLoadedNode
findObjectByTypeOnLeastLoadedNode
operation considers the=
system load when selecting one of the objects with the given type. If the =
registry is unable to determine which node hosts an object (for example, be=
cause the object was registered with a direct proxy and not an adapter ID),=
the object is considered to have a load value of 1
for t=
he purposes of this operation. The sample argument determines the interval =
over which the loads are averaged (one, five, or fifteen minutes). The oper=
ation returns a null proxy if no match was found.findAllObjectsByType
findAllObjectsByType
operation returns a sequence of prox=
ies representing the well-known objects having the given type. The operatio=
n returns an empty sequence if no match was found.findAllReplicas
findAllReplicas=
operation returns a sequence of proxies representing the individual=
replicas. An application can use this operation when it is necessary to co=
mmunicate directly with one or more replicas.Be aware that the operations accepting a type
parameter are=
not equivalent to invoking ice_isA
on each object to determin=
e whether it supports the given type, a technique that would not scale well=
for a large number of registered objects. Rather, the operations simply co=
mpare the given type to the object's registered type or, if the object was registered without =
a type, to the object's most-derived Slice type as determined by the regist=
ry.
Well-known objects are another IceGrid feature we can incorporate into o= ur ripper application.
First we'll modify the descriptors= to add two well-known objects:
<icegrid> <application name=3D"Ripper"> <node name=3D"Node1= "> <= ;server id=3D"EncoderServer1" exe=3D"/opt/ripper/bin/server" activatio= n=3D"on-demand"> &nb= sp; <adapter name=3D"EncoderAdapter" endpoints=3D"= tcp"> &nb= sp; <object identity=3D"En= coderFactory1" type=3D"::Ripper::MP3EncoderFactory"/> &nb= sp; </adapter> <= ;/server> </node> <node name=3D"Node2= "> <= ;server id=3D"EncoderServer2" exe=3D"/opt/ripper/bin/server" activatio= n=3D"on-demand"> &nb= sp; <adapter name=3D"EncoderAdapter" endpoints=3D"= tcp"> &nb= sp; <object identity=3D"En= coderFactory2" type=3D"::Ripper::MP3EncoderFactory"/> &nb= sp; </adapter> <= ;/server> </node> </application> </icegrid>
At first glance, the addition of the well-known objects does not appear = to simplify our client very much. Rather than selecting which of the two ad= apters receives the next task, we now need to select one of the well-known = objects.
findAllObjectsByType
The IceGrid::Query
interface provides a way to eliminate th=
e client's dependency on object adapter identifiers and object identities. =
Since our factories are registered with the same type, we can search for al=
l objects of that type:
Ice::ObjectPrx proxy =3D communicator->= ;stringToProxy("IceGrid/Query"); IceGrid::QueryPrx query =3D IceGrid::QueryPrx::checkedCast(p= roxy); Ice::ObjectProxySeq seq; string type =3D Ripper::MP3EncoderFactory::ice_staticId(); seq =3D query->findAllObjectsByType(type); if (seq.empty()) { // no match } Ice::ObjectProxySeq::size_type index =3D ... // ra= ndom number Ripper::MP3EncoderFactoryPrx factory =3D Ripper::MP3EncoderFactor= yPrx::checkedCast(seq[index]); Ripper::MP3EncoderPrx encoder =3D factory->createEncoder(= );
This example invokes findAllObjectsByType
and then randomly=
selects an element of the sequence.
findObjectByType
We can simplify the client further using findObjectByType
i=
nstead, which performs the randomization for us:
Ice::ObjectPrx proxy =3D communicator->= ;stringToProxy("IceGrid/Query"); IceGrid::QueryPrx query =3D IceGrid::QueryPrx::checkedCast(p= roxy); Ice::ObjectPrx obj; string type =3D Ripper::MP3EncoderFactory::ice_staticId(); obj =3D query->findObjectByType(type); if (!obj) { // no match } Ripper::MP3EncoderFactoryPrx factory =3D Ripper::MP3EncoderFactor= yPrx::checkedCast(obj); Ripper::MP3EncoderPrx encoder =3D factory->createEncoder(= );
findObjectByTypeOnLeastLoade=
dNode
So far the use of IceGrid::Query
has allowed us to simplify=
our client, but we have not gained any functionality. If we replace the ca=
ll to findObjectByType
with findObjectByTypeOnLeastLoade=
dNode
, we can improve the client by distributing the encoding tasks =
more intelligently. The change to the client's code is trivial:
Ice::ObjectPrx proxy =3D communicator->= ;stringToProxy("IceGrid/Query"); IceGrid::QueryPrx query =3D IceGrid::QueryPrx::checkedCast(p= roxy); Ice::ObjectPrx obj; string type =3D Ripper::MP3EncoderFactory::ice_staticId(); obj =3D query->findObjectByTypeOnLeastLoadedNode(type, IceGrid::LoadSample1); if (!obj) { // no match } Ripper::MP3EncoderFactoryPrx factory =3D Ripper::MP3EncoderFactor= yPrx::checkedCast(obj); Ripper::MP3EncoderPrx encoder =3D factory->createEncoder(= );
Incorporating intelligent load distribution is a worthwhile enhancement =
and is a capability that would be time consuming to implement ourselves. Ho=
wever, our current design uses only well-known objects in order to make que=
ries possible. We do not really need the encoder factory object on each com=
pute server to be individually addressable as a well-known object, a fact t=
hat seems clear when we examine the identities we assigned to them: E=
ncoderFactory1
, EncoderFactory2
, and so on. IceGrid's <=
a href=3D"/display/Ice35/Object+Adapter+Replication">replication features=
a> give us the tools we need to improve our design.