[MUSIC PLAYING]
Mike, your title is full of buzzwords-- objects
interfaces system software structure.
What are we going to learn today?
And what do these buzzwords mean?
Well, there's three things I'd like to cover today.
The first is issues relating to objects in the system software,
as opposed to just in object-oriented programming
languages.
The second is the Object Management Group's CORBA,
Common Object Request Broker Architecture,
and how it attempts to solve some of these problems
in system software for objects.
The third is how the CORBA architecture
is used to allow objects systems that already
exist to be connected together.
Now, I understand that in object speak
there are a lot of buzzwords that are
used in fairly ambiguous ways.
I have some fairly specific meanings to these terms.
And I'd like to describe them.
To me an object is a combination of computation and state
that are considered to be a unit.
An object has its own integrity in that you can't just
go in and manipulate part of it or associate the computation
with some other state.
The only way to manipulate an object
is by asking it, by sending requests to it,
and letting it perform the actual operations.
Objects are self-describing.
So when you walk up to an object,
you can find out what it is.
You don't necessarily have to know what an object is ahead
of time in order to use it.
An interface describes the shape of the object.
It includes things like what the operations are,
how to go about making requests on the object.
But what's important is that it doesn't include anything
about the actual implementation of the object.
It doesn't say anything about either the state
or the computation.
In a sense, the interface is the contracts between the object
and the clients of that object.
And interfaces are also used to categorize objects.
Objects that share parts of their interfaces
are generally considered similar and interchangeable.
Now, by system I mean a broader context
than you would normally have in a particular programming
language.
In a system, we're talking about a collection of components that
have a number of properties.
For example, they may be distributed.
They may be physically in different places
and have to find some way to communicate together.
Generally, a system is unbounded in the sense
that dynamically another part of the system
can show up that you didn't know existed before.
In general, systems are connected to people
that you don't necessarily trust.
So the various components have to be mutually suspicious.
They have to check to see if the person who's making a request
is in fact intended to be able to make a request.
The various components in a system are autonomous.
They start, stop, fail, recover independently,
as opposed to as a unit.
And what is probably the most subtle property of systems
but affects almost everything you do is that they evolve.
And there's no way to sort of with one flip of the switch
change the way a system behaves.
It's necessary to have simultaneously
some older parts of the system in some newer
parts of the system.
Now, by software structure I'm talking
about how the software is organized at the macro level.
I'm not as interested in this context in what happens
inside a particular component.
But I'm much more interested in the relationships
between components and how those components communicate
with each other and what sort of mechanisms
it takes to actually support components.
Now, the Object Management Group is a group
of people from different companies
that are all working on object systems.
And it turns out they're all working on different object
systems that are in some ways incompatible as people discover
new things and new ideas in object-oriented programming.
What the Object Management Group is trying to do
is to figure out how to make those systems more compatible.
And that happens in a couple of ways.
One way is to figure out how to evolve them,
so they are more similar.
The other is to figure out how to make them
so that even though they're different
they'll be able to play together.
In some sense, it's interesting how the Object Management Group
works at two different levels.
At the same time that they're working
on the common concepts for the software
to be able to have this object system to be able to talk
to this object system, they're also evolving inside
of terminology and way of describing things
so that we can have this person who's working on objects be
able to communicate adequately with this other person who's
working on objects.
Now, the Common Object Request Broker Architecture
is their basic object mechanism.
And it has the philosophy instead of the usual,
well, my object system is better than your object system
in these 12 ways, is well actually your object system
and my object system can play together.
So it turns out that partially by design and partially
because of the problem it's trying
to solve in plugging together object systems,
it turns out that CORBA contains a number of the solutions
to the problem of system software and objects.
Well, I do understand about object technology as used
in programming languages like C++ and Smalltalk.
But what's different about how you're
going to use objects for system software?
Some of the notions in object-oriented programming
languages are of particular relevance
to the system software environment.
The combination of the computation unit
in a piece of state is something that happens in systems all
the time, the notion of a process
in a traditional operating system.
The fact that there are well-defined
interfaces that you do information hiding.
That there can be multiple implementations
of the same service, that you can do extensions,
that you can have relationships between different types
of objects, these are all things that
are extremely important in a system software environment.
And they are provided by object-oriented technology
in traditional object-oriented programming languages.
On the other hand, there are some techniques
that are used in a programming language
context that really don't work very well in a system
environment.
For example, implementation inheritance, the ability
to reuse parts of other implementations,
turns out not to be a good idea in the system software
environment, because it creates a dependency between two
different pieces of software that is not well defined,
that is hard to evolve forward.
So that's a concept that would not
be appropriate in a system software environment that's
OK in a programming language environment.
It's also the case that types in object systems
generally need to be considered relative to who's
telling you what the type is.
If you trust the other component that you're interacting with,
then you can trust the types that are provided to.
But if that component might be a high school student breaking
into your system, you can't exactly
trust the fact, when it says that this
is their driver's license and this is their ID card.
It might have been forged.
Also, type checking in a system environment
is done locally rather than globally.
So the type checking is done within one program when it goes
to interact with the system.
But it can't be done sort of from one program
to another program.
The system can't provide all of those guarantees,
because one program could be working
in one language with one type system,
the other program could be working in another language
in another type system.
There really isn't a way to make all of that be consistent.
Now, if we look at how systems software has evolved,
we can see that there are some changes in structure that
lead us towards object-oriented solutions to system software
problems.
In the early mainframe computers, the system
itself has lots of functionality,
but it tends to be fixed and not easy to extend.
And programs generally go to the system to get their work done.
Well, when PCs came along, it turned out
that the system had very little functionality in it.
When people wanted to do particular things,
they would go out and they would buy an application.
And the application would have almost all of the functionality
that the user wanted in it.
And it was really a classic example,
if you get what you pay for.
If you bought this application, you'd have that functionality.
If you didn't, you wouldn't.
In a modern distributed system, there's
more of an open ended set of functions
that are available because you're connected
to a network of machines that can be potentially unbounded
and you could access a service somewhere out
there in the network.
In general, there's little distinction
in this environment between what's in the system
and what's an extension or what a particular user adds.
And access to services is sort of independent of where it is.
The environment is used as a whole.
So in this environment, it's sort of
natural to focus on software organized
as a set of components that we want
to be able to extend and replace these components,
that we want to be able to wander around the network,
discover components, and be able to use them.
And when I look at all these things,
it sounds like objects to me.
Well, I can see that objects would
be good for system software.
But what would you really want to use them for?
Well, in a word everything.
If I had to use two words, I'd probably say almost everything.
One of the things that objects allow us to do
is to unify things, both across different functionalities
and across different levels in different granularities
in the software environment.
So in a system context, being able to unify things
across different functionalities is extremely important.
A system environment is like a service industry.
Its main purpose in life is to provide access to things.
And it's really much more convenient
if I can use the same mechanism to get access
to a wide variety of services.
I don't want to have to use the telephone
to get to some things and the mail to get to other things.
I'd like to be able to get to all of the services
that I want in the same way.
So objects are general enough to be able to do this.
For example, we can use objects for OS concepts, operating
systems concepts, like files, users, mailboxes.
We can use them for application concepts, like paragraph,
cells, and bitmaps.
We can use them for communication pipes
and sockets and hotlinks that will give me
updated views of data.
Various services like printing or network mail or naming.
And all of these things can be done as objects.
And it gives us a uniform way of describing
these services, because of the fact that we've unified them
in this way.
Now, objects are also flexible enough
to cover different levels of services,
for example, from things as lightweight as toolkits,
a button object in a Windows system
where a synchronization variable can be an object,
things you would normally think of you
do a system call for like a page or an access right,
being able to access services that you would traditionally
do by way of an IPC or an RPC and authentication
service or a database server.
And, in fact, even things as large as whole programs
or multiple programs, a multimedia document that
might consist of a number of different parts
could all be viewed as objects.
By being able to span these multiple levels,
we provide the same mechanism that we can use,
the same way to access things independent of whether it's
a small thing or a large thing.
Well, you've probably heard the old saying
that to a person who has only a hammer,
everything looks like a nail.
And in a certain sense, objects can make this possible.
And applying object technology to the system environment
is in some sense trying to make everything look like a nail
and then make sure that everybody gets a hammer.
However, one of the things that we must be careful about
is trying to use a single solution to solve
too wide range of a problem.
You may notice that I have large hands.
And one of the problems that I have is one size
fits all gloves because they basically just don't fit.
The CORBA architecture has a variety
of places where different choices can be made,
different implementation techniques
can be used, according to the granularity, frequency, access,
and other properties of the objects.
So we get to be able to use the same framework,
the same architecture, the same mode of description
for objects.
But we have different abilities to implement them.
So that we can actually take advantage of the things
that we know about different kinds of objects.
[MUSIC PLAYING]
OK, so you told me that everything in a system
can considered be an object.
And now, you're talking about the CORBA.
Just what is the CORBA?
Well, I should take a minute and first tell you
the question that CORBA was designed to answer,
because OMG did a request for technology for a thing
that they called ORB, the Object Request Broker, which
is the basic mechanism to allow communication between objects.
And the process that we went through to submit CORBA
and to get a number of companies to work together
in defining CORBA as the answer to that question
caused us to pull together a lot of different ideas
in this common solution.
And it really helped make it so that CORBA
spans a wide range of solutions than it might have if we'd just
taken any individual object system
and called that the object request broker.
So the approach was to define whatever things we could
that were in common across the various object systems
that we knew about or that we were working with,
so that we could envision portable applications that
could work the same in these different environments
by relying on these common things,
and also so that we could have the basis for communication
between the different object systems.
We also then tried to define an architecture that
could embrace multiple systems, either
by having them adapt and support the CORBA
architecture without having to rewrite them from scratch
and also to allow future evolution, optimization,
and extension of systems as we learned more about them.
So CORBA is essentially an architecture designed
to unify multiple ORBs.
It's not just a single implementation
of an object request broker.
And it's designed to allow a portability of applications
across these different object systems
and interoperability between them.
So I understand that CORBA is the architecture for the ORBs
and the ORBs are what allow us to have objects.
So let's be more specific about what an object is.
Can you tell me in concrete terms just what is an object?
Well, yes or no.
In CORBA, the concrete definition of an object
is that it's an abstraction.
Fortunately, it turns out that there is a concrete thing
that you can put your finger on or your program
can put its finger on.
And that's what we call an object reference.
The client of an object accesses it
by using an object reference.
And that is something that you can
have and pass around and use to refer to the objects.
The only way to experience the real object,
however, is to do operations using the object reference.
It's sort of like the philosophical question
about the tree in the Northwoods and if it falls
does it make a sound.
And in the CORBA environment, it only
makes a sound if you have an object reference to it
and you call the listen operation
to determine that it's actually made a sound.
Otherwise, you can't tell what's actually there.
Now, as I mentioned, you can store,
pass around object references.
And the world is sort of connected together
by these object references.
Many clients can have the equivalent, a reference
to a particular object.
And there can actually be multiple references
to the same object.
For example, if the object is a file,
some object references might support the write operation.
Other object references would not
support the write operation.
So you can actually have multiple object references
for the same object.
In general, you don't know anything
about how the object is actually implemented or where it is.
You have the object reference.
You do an operation on it.
You get the answer back.
That's the extent that you can know about the object itself.
Now, the operation is a transfer of control and data.
So a client initiates the call.
The object reference determines exactly where
the call is going to go.
The operation that is specified determines
what code is actually going to be executed.
The parameters get transmitted to the destination
where the object is actually implemented.
Control is transferred there.
The implementation then runs, does
whatever the operation requested,
produces some results.
And then those results are transmitted back to the client.
Control transfers back to the client.
And then the client continues.
So the client effectively sees this
as a procedure call that it makes.
And the implementation sees it as a procedure
called that seems to come out of nowhere
into the implementation.
So from now on, when I say object, what I really mean
is the abstract entity that specified by an object
reference, because that's in practice what
it means in the CORBA context.
OK, so I understand I don't have an object.
I have an object reference.
So I have this object reference and now I
want to perform some operation.
How do I do that?
And what does the ORB do for me?
Well, the ORB does a number of things
to make it easy and convenient for you to do operations
on objects.
And I'll mention some of them in a little bit.
Basically, the assumption is that you're
writing a programming in your favorite programming language.
And for the sake of discussion, let's assume that C++.
You have some representation in your programming language
of the object reference.
And let's suppose that that's a surrogate object that you might
have in C++ that looks like a normal C++ object.
You then want to be able to call some code that
will do this work for you, because for your purposes,
you'd like it to look like a procedure call.
And in the C++ context, that's liable to be a method
on the surrogate object.
Now, that method we call a stub.
And it's going to do the work of figuring out
what other stuff it needs to do to actually cause the operation
to be transmitted to the implementation.
So the stub and the ORB cooperate
to actually cause the parameters to be transmitted
and the implementation to be located.
On the implementation side, there
is a thing called a skeleton, which
is analogous to the stub, which tries to then call
an appropriate method.
That's the implementation of that particular operation.
When that method returns, the skeleton
then takes the return values, passes them back using the ORB.
And then the stub on the client side
returns, passing those things back to the programming
language environment that the caller had,
presumably in a way that's convenient for that programmer.
So the goal is for the client to think that it's simply
calling a local routine and the implementation
to appear to be called locally.
And the ORB plus the stubs plus the skeleton sort
of cooperate to provide the glue to make
these two pieces fit together.
Note that the interfaces to the stubs of the skeletons
are, in fact, language specific.
We call it this a language mapping.
And it can be customized for a particular programming
language.
So if your programming language has objects,
you're liable to have a language mapping that makes
it look more like objects.
If you're programming in FORTRAN or something like that,
well, you'll have to use your imagination
to see where the objects are in this program.
CORBA currently defines a standard mapping for C.
And they're currently working on C++ and some other mappings.
Now, note that the client and the implementation mapping
can be completely different.
And the ORB has to solve the problem of having a COBOL
program call an implementation that's written in Smalltalk,
and it does solve that problem.
An important feature of the stubs
is that while the interface to the client
and to the implementation is the same,
the interface between the stub or the skeleton and the ORB
is specific to the ORB implementation.
This is really important because the ORBs may be
implemented quite differently.
And the right thing to do to map a particular programming
language into a particular ORB may be
different in different cases.
So the stubs and the skeletons take
care of this problem of mapping the language environment
into the particular ORB.
Neither the clients nor the implementation
have to be concerned with generating the stubs
or the skeletons however.
So that sounds pretty easy.
All I have to do to do an operation on object references
make a function call.
Is that really all there is to it?
Well, for the users, that's pretty close.
Unfortunately, for ORB implementors
there's more to it.
Note that I didn't say anything about where the client
and where the implementation are.
The ORB has to keep track of this.
As the object reference gets passed around,
it's got to be able to get back to wherever
the implementation happens to be,
whether you're crossing machine boundaries.
If you're going onto a machine with the different
architecture, it's going to have to figure out
how to translate the parameters into the data formats that
are appropriate for that architecture.
There may be differences in operating systems,
et cetera, that have to be overcome.
In fact, the ORB manages the object references persistently.
So you could have an object reference,
save it, come back tomorrow.
That object reference is still supposed
to work unless that object has been destroyed.
So you can store object references
in the middle of files and documents or whatever.
The ORB also keeps track of the type of object references
because you want to know what kind of an object
this object reference refers to.
Besides invocation, the ORB also provides
functions like creation and deletion of objects.
And when you do an operation, if the implementation,
because you got bored and didn't use
the object for a couple of weeks,
isn't active at the moment, the ORB
will actually start up the implementation in order
to get it running before it delivers the request.
There's also an interface definition language
for specifying the interfaces defining
what the operations are.
That interface definition language
is used to produce the stubs and the skeletons.
And that information is also stored
in an interface repository.
So we can find out what interfaces
are supported by a particular object at runtime.
The ORB supports the notion of object adapters.
So there can be different styles of implementations as well.
So the ORB provides a number of functions
that make it easy for the client in the implementation.
But this means that the orb is responsible with the stubs
in the skeleton in the interface repository for keeping
track of this information for the convenience of the client
and the implementations.
[MUSIC PLAYING]
So you mentioned interface definition language.
Does this mean that I have to learn another new programming
language to use the system?
Well, it is necessary to learn another language.
But you actually don't program in it.
The Interface Definition Language, or IDL,
some people say IDL, is used for defining the operations
on objects.
I'm actually fond of the name we used for the research
prototype of this language, which was contract,
because the interface definition language is trying to express
the agreements between the clients
and the implementation of the objects.
It defines the things that most object systems
have, like operations, what they do, signatures,
how you actually request them, exceptions,
what might go wrong when you try to do a particular operation.
It groups operations together in interfaces
and allows there to be relationships
between interfaces.
There's interface inheritance, which
expresses sort of what operations
are in common between two different interfaces.
On the other hand, there are some important things
that interface definition language doesn't say.
For example, it doesn't name any instances of objects.
You have to get those yourself.
It doesn't say anything about how the object is implemented,
where it is.
And also, it's important to note that there's
no correspondence between an interface
and any particular implementation.
There can be a large number of different, completely unrelated
implementations of the same interface.
And there may be some interfaces for which there
are no actual implementations.
So whether your implementation of an object is written in C
and it's inside your program, or whether it's written in FORTRAN
and it's on Mars, it could be the same interface
and you wouldn't notice any difference.
Now, as far as programming, there
are no executable statements in IDL.
So in that sense, it's not a new programming language.
You write programs in your own language.
And what you have to understand is the mapping
that IDL has to the language that you want to program in.
Now, IDL has C or C++ style syntax,
for which we sort of apologize.
It at least has a syntax that's familiar
and data types that are familiar.
It continues to have all the same problems that C and C++ do
in that space.
But we decided we'd rather not make new mistakes in that area.
Now, it turns out that inheritance
is crucial for systems.
And so I want to say a little bit about that
and how inheritance is used in IDL.
It's often the case in systems where
you want to deal with objects without knowing
their exact type.
For example, if you have something that's a document,
and a spreadsheet is a kind of document,
and a printer can print a document,
then that means a printer can print a spreadsheet.
With IDL we're able to express those relationships precisely.
When you use interface inheritance
you're asserting that the spreadsheet object, in fact,
can be used and supports all the operations that a document has.
Since all that anybody can know about an object is
what's expressed in the interface,
if they see an object that has a document interface
and that's what they use, they don't
care what else might be true about that particular kind
of document.
Well, I understand about interfaces.
I write C++ class definitions and put them in header files.
Why are you putting so much emphasis
on interface in this system?
Well, in system software we use these interface techniques
over and over again.
There's generic services, as I mentioned, like printer.
If you look at a system today, like Unix,
the only way you can really tell that a file and a pipe
and a socket are really something in common-- well,
you can look at the code.
Or you can read the man pages and say, boy, they
seem to say a lot of the same things
about these different concepts.
This happens all the time and systems
that we build different implementations
of the same kinds of things.
We want to have them related together,
and the interface scheme allows us to express that directly.
It's also the case that we want to allow
alternate implementations of the same interfaces.
So if, for example, there's a mouse trap interface,
you can look at that interface and say, hey, I
can build a better mouse trap.
You can do that, and when you provide that service, when
you provide that mouse trap, what you know
is that all of the other parts of the system that expect
to use mouse traps will work with your mouse trap,
because the only thing they could possibly know
is the interface.
And if you've successfully implemented that interface,
those parts of the system, more or less,
have to work with your flavor of mouse trap.
It's also the case that systems evolve.
And we often will be in a situation
where we want either newer versions of the same interface
or extended interfaces.
We want to make it so that clients of the old interface
can use the new objects without worrying about the extensions.
At the same time, we want to make it
so that clients of the newer interfaces
can, in fact, use the new features.
Extensions are another thing that are common in systems.
These are things where you want to add value added
versions of existing facilities, for example,
if you want to have a compressed version of files
so that they would take less space on the disk.
So new objects that are this new extended service
will work with other services because they're still files.
But you may decide, well, jeez that I
don't want to spend all my time compressing and uncompressing
this file, so I'm going to do an operation
on this compressed file that says,
well, don't compress it because I want it to run faster
or whatever.
So you want to access the extended functionality
and be able to have additional control on that service.
And the important thing is that all of these things
end up being first class.
Because they're all described in the same way,
because they're all accessed the same way, whether it's
a built-in system function, whether it's an installation
specific function, whether it's something
that an individual user has, whether it's
permanent or temporary or conditional used
in certain circumstances, they're all done the same way.
Another key aspect about how IDL does inheritance
is that it uses multiple inheritance.
And, in fact, we encourage the use of multiple inheritance
for interfaces.
With single inheritance, you have
to decide where to put the various interfaces
in the interface tree.
If you put things too close to the root,
you end up having things that are not necessarily
making sense for all of the objects that are
below them in the type tree.
On the other hand, if you spread these interfaces out
at multiple places in the type tree,
you no longer have the type relationship between those two
different sub-trees.
For example, if you consider the traditional sort of I/O stream
and file kind of relationship, where would you add replication
to this picture?
Should you add it below I/O stream?
Should you added above I/O stream
so that file can inherit i also?
Do you add it below I/O stream and below file, in which case
a replicated I/O stream isn't the same type
as a replicated file?
I actually have a sort of a hobby of class hierarchy
archaeology.
It's sort of like heirarcheaology.
I don't know if that's a new word.
Basically, when people are defining single inheritance
hierarchies, there's some point in time
after which it's difficult to change the hierarchy.
And that's because while you ship some products,
there are a lot of people that are depending upon it.
And so you can't add new things to the base classes.
And it's always interesting to look and see
what things got into the base classes
because it was easy to do and what things didn't get in later
when it was too hard to do.
With multiple inheritance a new interface
becomes a new root node in the interface graph.
And it can be inherited wherever it's appropriate.
So you can use objects that support the new interface
independent of what other interfaces they might have
or where they are in the previous parts of the type
graph.
In the example of replicated file,
a replicated file can be replicated I/O stream.
And you can use replicated things independent
of what other things they think might happen to be.
Now, although these sort of type and interface and inheritance
issues are important for programming languages,
they are really paramount in software systems,
because although it may be possible to recompile
a whole program, it's not really practical to ever recompile
a whole system in all of the software that
will depend upon the system class hierarchy.
So there are so many different suppliers of software,
you have to be able to use software without knowing more
than is necessary.
You have to allow other people to have a stable base
to depend upon.
And that means that you have to spend
a lot more time making sure you get the interfaces right.
But, as you pointed out earlier, distributed system
is very dynamic and would seem to require a dynamically typed
object system.
But what you're describing seems to be statically typed?
Isn't that a problem?
Well, the system is definitely statically typed.
And there are sort of two reasons for that.
The second is that, in fact, we managed
to get a fair amount of dynamic behavior in spite of the fact
that our types are static.
But the first reason that I want to say
is that static is actually better for our problems.
We want to have type systems that actually document
and guarantee the contract analogy,
what the objects are going to be able to do.
And so we don't want to have the opportunity for someone
to change things without having that reflected somehow
in the type system.
A lot of time, software components
are going to first be introduced to each other
at the customer site.
And the customer is going to want
to know who to blame if these two things don't work together.
And the type system is a way that says, well, look,
here's how it's supposed to work.
So that guy is misbehaving, or this guy is misbehaving,
so you know which customer service number to call.
We can still have a lot of generality
with inherited interfaces because there
can be lots of differences among implementations.
And, of course, we do have the opportunity
to do checking in compiled languages
by having static type.
So before you ship your product, you
can know that it's going to do the right things
relative to the system defined interfaces.
And if there are bugs to be found,
they're probably going to be in someone else's software.
But it turns out that we also provide with IDL and in the ORB
a number of features that are normally associated
with dynamic type systems.
For example, new interfaces and implementations
can be added without disrupting the type system as I mentioned
with multiple inheritance.
It's possible to discover at run time an object
and find out what its type is and then
figure out how to actually use it
using the interface repository.
And you can delay the determination
of the exact type of an object for a long period of time.
So you can use objects without actually caring
what their exact type is.
There's two ways of dealing with types dynamically.
The first is to use the get interface operation
to get the interface repository information.
That's not always what you want because it's
going to give you a very specific answer to the question
of what type are you.
If what you need is a file and what
you get from the interface repository is something that
says that it's a vacuum cleaner, you
may have to run around in the type hierarchy
to figure out, OK, where in vacuum cleaner
do I find the file interface?
But it does have all the information that you need.
So if you stumble across this new object and you say,
oh, it's vacuum cleaner.
You can go and find out what sorts
of things, what sorts of operations, that object
might actually support.
A more common way to deal with types
dynamically is an operation that we call narrow,
which is a variation on the normal programming
language narrow, because of the fact that you can do it
across multiple parts of the tree with multiple inheritance.
And that is that you ask if the object has
a particular interface.
And then it gives you that particular interface view.
So if you have this thing, and it turns out
to be a vacuum cleaner, and you're asking if it's a file,
I can point you to the part of it that looks like a file
and not have to bother you with the fact
that it really is a vacuum cleaner.
If you were interested in using file operations,
you're then able to use them, ignoring
the rest of the details of what the object is.
[MUSIC PLAYING]
OK, well, we talked about how I can use an object that's
out there in the system.
But now it comes to the point where
I want to start defining my own objects.
How do I go about implementing an object?
Well, an implementation is pretty much
guaranteed to be more complicated than a client.
We spend a lot of time trying to make
it easy for the customers in some sense of objects.
But the implementation, of course,
has to do the real work.
It has to deal with creating and deleting and preserving
the state and all those sorts of things.
But the core of an implementation of objects
is very similar to what an implementation of objects
would be in a typical object-oriented programming
language.
Basically, the ORB and the skeleton
are set up to make it so that you simply
plug in the methods that do the operations into the ORB.
However, because we're dealing with a system,
there are two extra concerns.
The first is security.
This is this high school student out there
that's trying to break into your system.
And you've got to decide who's allowed to do which operations.
And there's also the issue of asynchrony,
because you can have several requests made at the same time.
Both of these issues can be dealt
with in either a simple or complex way depending
upon the object.
For example, if it's a really simple object,
I can just acquire a lock and then
release it at the beginning and end of each method,
and that will pretty much solve the problem.
For more interesting objects, I may, in fact,
want to release the locks partway
through an operation to allow other operations to start
or make progress.
Similarly, with the security issue,
I could simply delegate it to an authentication service
and say, protect me.
Or I may choose to have more fine grained control,
and I'm going to look at your ID and say, well, this
is a good thing for you to be able to do?
And then I'm going to go look at my database
and make my own decisions.
So you have a range of possibilities.
And there is a good opportunity for using some standard system
facilities to make life easier.
Probably the major difference between a programming language
object implementation and a CORBA object implementation,
however, is activation.
Because objects are persistent, they can last a long time,
you, as an implementation, may get bored or tired
or your system may crash, and then some time later, a request
comes in.
Well, the ORB provides the means for you
to get started, get your state set up
again, before it actually delivers that request to you.
This involves a number of up calls from the ORB
and some down calls back to the ORB
to register your implementation and say
that you're ready to go.
This is another place where the architecture provides
a number of hooks so that you can do it
in some very gentle and complicated ways
if you need to.
But it's also a place where if you're using standard storage
facilities or if you're doing something pretty traditional,
you can probably find the right routine to just plug
into your implementation and let somebody else do
the actual work.
Well, you've described a wide range
of choices I could make when I do an object implementation.
But what do I have available to me to make those choices?
What kind of choices do I have?
Different objects implementations
can be quite different and have some real differences in what
they expect from the ORB.
And to avoid having to have one size fits all
object implementations, we've invented a concept
that we call the object adapter.
And this really is an important architectural feature
of the CORBA system, because it's
the thing that allows us to have sort
of multiple sizes of interfaces to the ORB.
So an object adapter defines the interface to the ORB,
including operations for creating and deleting
and moving objects.
And it also pins down the exact structure of the skeletons.
All the skeletons for all the object adapters
are probably going to be pretty similar,
but there may be some important differences in exactly
how the request is delivered.
The basic object adapter is the only one
that the Object Management Group has yet approved.
But we're investigating a couple of others already.
We probably don't want a large number of objects adapters,
because in order to make portable object
implementations, all of the ORBs have
to support all of the possible object adapters.
So in general, you only want to have an object adapter
if there's really a radically different set of requirements
from the ORB for a particular object implementation.
OK, so you've mentioned the one object adapter,
the basic object adapter.
What kind of object implementations
would I use that object adapter for?
Well, the basic object adapter is
aimed at what I might call server style implementations.
It's sort of in the middle of the range of granularity.
We're not talking about nuclear aircraft carriers as objects,
but we're also not talking about subatomic particles as objects.
It's things like documents or spreadsheet cells
or users or system concepts like that.
The assumption is that the implementation of the object
can be protected from the client,
that it can have this general activation
mechanism of being started up independent of other objects.
The ORB actually will remember about 1k bytes of state
for each object if you want.
And it assumes that there is external visibility
of those objects, that they are going to be passed around.
And so it also has a security model that
assumes access control lists.
The performance expectations for this kind of object
is probably on the order of a few dozen invocations
per second.
You probably aren't going to get up to a million.
But you can probably do something in that range.
It shouldn't take an hour and a half to do an invocation.
It has actually a rich set of activation mechanisms,
because there will be different kinds of implementations
for objects.
A typical one is that a server might have
many objects implemented in it.
So when you start it up for the first object that you use,
it says, oh, by the way, I implement all these other ones.
So you don't have to do the extra work of activating
those objects separately.
On the other extreme is an object
where each operation will start up a different program.
This would tend to be for a larger size
object, where you would have a program
to print or edit the object.
We expect the basic object adapter
to be able to be used for most common application in system
objects.
And that's why it was the first one that we designed.
Well, in fact, it seems to cover a very wide range of objects.
Can you give an example of another kind of object adaptor
that would be quite different?
Well, the next one that we're likely to try to standardize
is the one that we call the library object adapter.
And this is for objects that I might call toolkit style
implementations, things that you would normally expect
to be in an object library.
The objects are fine grained and very lightweight,
things like buttons in a Windows system toolkit or simple data
structures.
The difference in these objects is
that they're expected to co-reside
with the client, which means that you have
to be very for writing the environmental impact
statement of the object because it
could have impact on the client that's trying to use it.
But it also has a simpler notion of activation
because it's assumed to be activated with the client.
So you don't have to do a lot of separate work.
There's no ORB state.
We're trying to keep these things lightweight.
And the assumption is that these aren't
going to be externally visible, although there
are ways to make them visible if you pass them out.
There's no security.
If you're going to live with the client,
it presumably can do to you whatever it wants.
And the main reason for supporting this different style
of object is that we expect to have much stronger performance
requirements to be able to do thousands
of invocations per second.
And we can probably do this because there isn't security.
There isn't the extra state.
And they have limited visibility.
But these do sound quite different.
Won't that mean that when I use a BOA object or a LOA object,
I'll have to do different things to use those different objects?
Well, no, the whole goal here is to make it
so that clients use the objects the same way
and aren't aware of the choice of object adapter.
This is why we want to make it so that if a client has
an object that happens to be implemented by the library
object adapter and it passes it, say,
on a call using an object that's implemented by the basic object
adapter that's going out to Mars, that we
will do the right thing.
We'll make that library adapter object
be available through a basic object adapter
so that it can be accessed externally.
On the other hand, we do win because for objects
that tend to be local and tend to be accessed locally,
we get the performance advantage,
although we do have to worry a little bit more
about the environmental impact statement.
But we still do have the generality.
So if it's an occasional thing that we
pass one of these objects to Mars,
we figure out how to make that work.
But you can see from these two examples
how important it is to have an object adapter.
These are quite different sets of requirements.
Unfortunately, if we would have burdened all objects
with having security, for example,
that would make the fine-grained objects much more heavy weight.
On the other hand, if we constrained all objects
to not have any state and to not do activation,
that would make it hard to construct
the range of server style objects
that we'd like to be able to do.
OK, so I'm implementing an object.
And you've told me about interface inheritance.
But you still haven't told me how I might
use implementation inheritance?
Well, that's because no one uses implementation inheritance.
Basically, I'm sort of opposed to implementation inheritance,
although there's probably some amendment
to the constitution that says I have to let you do it.
The problem is that when I do implementation inheritance,
I create dependencies between the part
that I inherit in the new part that I write that is not
written down anywhere.
The guy who wrote the original implementation
doesn't know what parts I depend on
and what parts are safe for change.
And I'm not sure what parts might be changed tomorrow.
But it turns out that the CORBA doesn't say much
about implementation inheritance either way.
The requirement is that when you register an implementation
for object, you have to provide an implementation for all
of the operations.
Where those methods come from and whether those methods
are shared with other object implementations, we don't know.
And in some sense, we don't care.
If you have a programming language that
supports implementation inheritance
and you want to build a collection of implementations
that reuse parts of the code, that's OK
as far as CORBA is concerned.
But it's important not to assume that because you
used implementation inheritance in your implementation
that that's going to have any effect
on other implementations, because there's
no way to force other implementations to reuse
any parts of your code.
It's always possible for them to write it all from scratch.
But what I might suggest as an alternative
to implementation inheritance is what
is usually called delegation.
And it's effectively using another object
for some of the methods, performing operations
on that other object to get those methods.
It's safe because you're using that other object abstractly.
You're just invoking operations the same way anybody else
might be able to.
And the fact that you're doing your operations by invoking
its operations, that's fine.
So my standard phrase in this area
is that I want objects to use, not to be reused.
Reuse implies that I'm going to do something partial
with the implementation.
It requires me to understand something
about the implementation.
And it adds this dependence.
Use implies that I'm going to have a coherent abstraction.
I'm just going to use the interface,
and I get the clean separation that I want.
[MUSIC PLAYING]
Well, we've talked about how I could use the objects out there
in the world.
And you've told me how I can implement some of those objects
out there.
But what about this ORB that makes all this happen?
Can you tell me about what's going on inside the ORB?
Well, sure, now, remember CORBA was
designed to allow the integration
of different systems.
So it's hard for me to tell you what any one of them will do.
But we do have some experience with the style of system.
And we do know that there is sort
of a range of possibilities.
Remember, we want to be able to adapt some existing systems,
but we probably will build some from scratch.
And it might be interesting to think
about what some of those ones that would be built
from scratch might look like.
Currently most ORBs are designed to be systems that
are approximately RPC style.
The object references used to locate the server.
You use the RPC mechanism to actually pass the request.
It has the advantage that RPC systems already exist.
You have the advantage of building
on widespread technology that works in a distributed
environment.
And it's good to have that to fall back on.
If you build an ORB that is better in some way
but more limited, that's going to make it hard
for you to get access to all the things out there.
And having another ORB that you can fall back on
to get access to that wider set of services
is always a good idea.
The ideal ORB is probably something
like an object microkernel.
You can make it be efficient.
You can make it be secure.
You can forward a request up to networking software
when you have the cross machine boundaries.
And here in some labs, we have a research prototype on Spark
that can do a cross address space call in about 100 cycles.
So we can see that we can get fairly reasonable performance
if we design the microkernel to be object oriented
from the start.
In addition, there are a number of other optimizations that
are possible, for example, being able to pass parameters
and shared memory and having memory
shared between the client and the implementation.
Object systems have sort of gotten a bad rap
for being slow.
And we've spent a lot of time in our research
and also in doing the CORBA architecture to make sure
that there is room there for optimizations,
that we can get the performance that we
need to make these things be used all the time every day.
And it's important to understand that as we
do these kinds of optimizations, as we change
from RPC to microkernel to shared memory ORB
implementation, the client and the implementation
stay the same.
So what the client sees, what the implementation sees,
what the code they write is is the same.
And we're replacing the ORB and the communication mechanisms
between in a transparent way.
Now, it turns out the clients can use different ORBs
simultaneously.
And this is basically an application
of object-oriented design to the way you access the stubs.
And by making the particular stubs be associated
with the particular piece of data that's the object
reference, we can make it so that you
can get to the right stub for that object reference
right away and then be doing code
that is appropriate for that particular implementation.
So you can see that we provided a number of places
in the architecture where we can get leverage on performance--
in the ORB itself, by using stubs and skeletons to allow
us to have the ORB implemented as efficiently as possible
and bringing that right up to the programming language level,
in the structure of the implementations,
in the interfaces provided by the object adaptor
to make them allow us to have the best
possible implementations, and in the support for multiple ORBs
so that you could have ORBs that are optimized
for different situations and could
have different characteristics.
Well, you talked about all these different ORBs.
It would seem important that these ORBs talk to each other.
So what does OMG and the CORBA say about interoperability?
Well, the first thing to understand
is that useful interoperability has to be brought up
to the application level.
What really matters is can my word
processor use your spreadsheet?
And there's a lot of agreements that
have to happen there-- data formats, protocols,
how we rendezvous, update policies, and stuff like that.
CORBA addresses it at the lowest level to get us going.
And sometimes it's like the telegraph.
We've agreed on Morse code.
So we have the basic communication mechanism
that we can use and everybody knows
how to transmit Morse code back and forth,
whether they're doing it over radio waves or over wires.
But unfortunately, we haven't yet agreed,
are we speaking French or are we speaking English?
And OMG is working on some of these higher level
agreements that are going to make interoperability up
to the application level actually be possible.
However, we have focused on making it possible for ORBs
to connect together, because we agree that the things being
communicated are object in locations and parameters
that are defined in the interface definition language.
We can plug them together using something
very analogous to a telegraph repeater, which
has sort of a clicker connected to another key that
allows you to transmit from one medium to another.
We can have the same concept in ORBs, which we call a gateway.
A gateway knows how to understand request from the two
ORBs that it's connecting together
and translates the data formats and passes the information.
It takes a request from one side and turns it
into a request on the other side.
One of the subtle parts about this
is that when I pass an object reference from one ORB
to another, what I want to do is replace it
with an object reference that points back to the gateway.
So that when I'm over in this ORB and I make a request,
I get back to the gateway and then
it knows how to pass that request onto the object
in the other ORB.
So with CORBA, a program can use objects
that are implemented in different ORBs
either because the environment that that program is running in
has access to multiple ORBs or because it's accessing objects
that have been passed to it from another ORB through a gateway.
And as I mentioned earlier, the CORBA
is trying to integrate different object systems.
It turns out now we can look back and see that there's
actually three different ways that I could take an existing
object system and figure out how to get a connect into the CORBA
architecture.
One is I can make the object system appear
as a normal object implementation
and have its objects registered with the ORB
and exported through it.
A second way is to use a special object adapter, which
would allow any differences between the type system
and the method of operation of the new object system
to be able to be translated into the ORB.
And the third way is that I can think of the object system
as an ORB in itself and then simply build
a gateway between that object system and the ORB.
Whichever way works best is going
to depend upon what the object system is,
what its characteristics are, and how much freedom you
have to go back and change it.
I see we have an object system for system software.
But where does the CORBA go from here?
Well, currently there is about a half a dozen--
there may be more since they don't all
have to register with me--
implementations in progress in different companies.
Some of them are from scratch.
Some of them are adapting existing object systems.
There's currently widespread support for this
because it does meet the needs of object systems.
It allows objects systems to work together.
And it's the first object architecture
that seriously addresses openness
and the evolution of software in the presence
of strong interfaces.
So the OMG is working to refine CORBA,
to add some more language bindings and things like that.
And it's also using CORBA and IDL
as the basis for defining the further services that they're
trying to standardize on.
[MUSIC PLAYING]
So to try to summarize, we've sort of
talked about the challenges of system software for object
technology and talked about how important interfaces
are for this definitional property,
providing this taxonomy to the services and components,
and focusing on the issues of multiple, evolving, and open
implementations.
We've developed language bindings
to try to make this stuff as convenient
as possible for both the clients and the implementations.
And the architecture supports a variety
of different implementations in objects themselves, in the ORB
itself, and provides lots of opportunities
for optimization and extension.
And it does accomplish one of the major goals of OMG
in asking for the ORB in the first place, which
is it provides a variety of choices for how object
systems connect together.
[MUSIC PLAYING]