Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migration of unmigrated content due to installation of a new plugin

Znav
nextThe C++ Utility Library
prevAsynchronous Method Dispatch (AMD) in C++

This page presents the source code for a C++ server that implements our file system and communicates with the client we wrote earlier. The code is fully functional, apart from the required interlocking for threads.

...

Info

The server code shown here is not quite correct as it stands: if two clients access the same file in parallel, each via a different thread, one thread may read the _lines data member while another thread updates it. Obviously, if that happens, we may write or return garbage or, worse, crash the server. However, it is trivial to make the read and write operations thread-safe: a single data member and two lines of source code are sufficient to achieve this. We discuss how to write thread-safe servant implementations in Threads and Concurrency with C++.

On this page:

Table of Contents
maxLevel2

Implementing a File System Server in C++

...

In similar fashion, the remainder of the code creates a subdirectory called Coleridge and, within that directory, a file called Kubla_Khan to complete the structure in the above illustration.

Ztop

Servant Class Definitions in C++

...

Wiki Markup
{zcode:cpp}
namespace Filesystem {
  class DirectoryI : virtual public Directory, virtual public NodeI {
  public:
    virtual NodeSeq list(const Ice::Current&) const;
    DirectoryI(const Ice::CommunicatorPtr&, const std::string&, const DirectoryIPtr&);
    void addChild(NodePrx child);
  private:
    NodeSeq _contents;
  };
}
{zcode}

Ztop

Servant Header File Example

...

Wiki Markup
{zcode:cpp}
#include <Ice/Ice.h>
#include <Filesystem.h>

namespace Filesystem {
  class DirectoryI;
  typedef IceUtil::Handle<DirectoryI> DirectoryIPtr;
  
  class NodeI : virtual public Node {
  public:
    virtual std::string name(const Ice::Current&);
    NodeI(const Ice::CommunicatorPtr&, const std::string&, const DirectoryIPtr&);
    void activate(const Ice::ObjectAdapterPtr&);
  private:
    std::string _name;
    Ice::Identity _id;
    DirectoryIPtr _parent;
    NodeI(const NodeI&);            // Copy forbidden
    void operator=(const NodeI&);   // Assignment forbidden
  };
  
  typedef IceUtil::Handle<NodeI> NodeIPtr;

  class FileI : virtual public File, virtual public NodeI {
  public:
    virtual Lines read(const Ice::Current&);
    virtual void write(const Lines&, const Ice::Current& = Ice::Current());
    FileI(const Ice::CommunicatorPtr&, const std::string&, const DirectoryIPtr&);
  private:
    Lines _lines;
  };
  
  typedef IceUtil::Handle<FileI> FileIPtr;

  class DirectoryI : virtual public Directory, virtual public NodeI {
  public:
    virtual NodeSeq list(const Ice::Current&);
    DirectoryI(const Ice::CommunicatorPtr&, const std::string&, const DirectoryIPtr&);
    void addChild(const Filesystem::NodePrx&);
  private:
    Filesystem::NodeSeq _contents;
  };
}
{zcode}

Ztop

The Servant Implementation in C++

...

Wiki Markup
{zcode:cpp}
Filesystem::Lines
Filesystem::FileI::read(const Ice::Current&)
{
    return _lines;
}

void
Filesystem::FileI::write(const Filesystem::Lines& text, const Ice::Current&)
{
    _lines = text;
}

Filesystem::FileI::FileI(const Ice::CommunicatorPtr& communicator,
                         const string& name,
                         const DirectoryIPtr& parent)
    : NodeI(communicator, name, parent)
{
}
{zcode}

Ztop

Implementing DirectoryI

The implementation of DirectoryI is equally trivial: the list operation simply returns the _contents data member and the constructor passes its arguments through to the NodeI base class constructor:

...

The only noteworthy thing is the implementation of addChild: when a new directory or file is created, the constructor of the NodeI base class calls addChild on its own parent, passing it the proxy to the newly-created child. The implementation of addChild appends the passed reference to the contents list of the directory it is invoked on (which is the parent directory).

Ztop

Implementing NodeI

The name operation of our NodeI class is again trivial: it simply returns the _name data member:

...

Wiki Markup
{zcode:cpp}
void
Filesystem::NodeI::activate(const Ice::ObjectAdapterPtr& a)
{
    NodePrx thisNode = NodePrx::uncheckedCast(a?->add(this, _id));
    if(_parent)
    {
        _parent?->addChild(thisNode);
    }
}
{zcode}

...

Wiki Markup
{zcode:cpp}
#include <IceUtil/IceUtil.h>
#include <FilesystemI.h>

using namespace std;

// Slice Node::name() operation

std::string
Filesystem::NodeI::name(const Ice::Current&)
{
    return _name;
}

// NodeI constructor

Filesystem::NodeI::NodeI(const Ice::CommunicatorPtr& communicator,
                         const string& name,
                         const DirectoryIPtr& parent)
    : _name(name), _parent(parent)
{
    // Create an identity. The root directory has the fixed identity "RootDir"
    //
    _id.name = parent ? IceUtil::generateUUID() : "RootDir";
}

// NodeI activate() member function

void
Filesystem::NodeI::activate(const Ice::ObjectAdapterPtr& a)
{
    NodePrx thisNode = NodePrx::uncheckedCast(a->add(this, _id));
    if(_parent)
    {
        _parent->addChild(thisNode);
    }
}

// Slice File::read() operation

Filesystem::Lines
Filesystem::FileI::read(const Ice::Current&)
{
    return _lines;
}

// Slice File::write() operation

void
Filesystem::FileI::write(const Filesystem::Lines& text, const Ice::Current&)
{
    _lines = text;
}

// FileI constructor

Filesystem::FileI::FileI(const Ice::CommunicatorPtr& communicator,
                         const string& name,
                         const DirectoryIPtr& parent)
    : NodeI(communicator, name, parent)
{
}

// Slice Directory::list() operation

Filesystem::NodeSeq
Filesystem::DirectoryI::list(const Ice::Current& c)
{
    return _contents;
}

// DirectoryI constructor

Filesystem::DirectoryI::DirectoryI(const Ice::CommunicatorPtr& communicator,
                                   const string& name,
                                   const DirectoryIPtr& parent)
    : NodeI(communicator, name, parent)
{
}

// addChild is called by the child in order to add
// itself to the _contents member of the parent

void
Filesystem::DirectoryI::addChild(const NodePrx& child)
{
    _contents.push_back(child);
}
{zcode}

Ztop

See Also

Zret
Znav
nextThe C++ Utility Library
prevAsynchronous Method Dispatch (AMD) in C++