Save datastream content and any changed datastream profile
        information to Fedora.

        :rtype: boolean for success

def save(self, logmessage=None):
        """Save datastream content and any changed datastream profile
        information to Fedora.

        :rtype: boolean for success
        """
        if self.as_of_date is not None:
            raise RuntimeError('Saving is not implemented for datastream versions')

        save_opts = {}
        if self.info_modified:
            if self.label:
                save_opts['dsLabel'] = self.label
            if self.mimetype:
                save_opts['mimeType'] = self.mimetype
            if self.versionable is not None:
                save_opts['versionable'] = self.versionable
            if self.state:
                save_opts['dsState'] = self.state
            if self.format:
                save_opts['formatURI'] = self.format
            if self.checksum:
                if self.checksum_modified:
                    save_opts['checksum'] = self.checksum
            if self.checksum_type:
                save_opts['checksumType'] = self.checksum_type
            # FIXME: should be able to handle checksums
        # NOTE: as of Fedora 3.2, updating content without specifying mimetype fails (Fedora bug?)
        if 'mimeType' not in save_opts.keys():
            # if datastreamProfile has not been pulled from fedora, use configured default mimetype
            if self._info is not None:
                save_opts['mimeType'] = self.mimetype
            else:
                save_opts['mimeType'] = self.defaults['mimetype']

        # if datastream location has been set, use that for content
        # otherwise, use local content (if any)
        if self.ds_location is not None:
            save_opts['dsLocation'] = self.ds_location
        else:

            save_opts['content'] = self._raw_content()

        if self.exists:
            # if not versionable, make a backup to back out changes if object save fails
            if not self.versionable:
                self._backup()
            # if this datastream already exists, use modifyDatastream API call
            r = self.obj.api.modifyDatastream(self.obj.pid, self.id,
                    logMessage=logmessage, **save_opts)
            # expects 200 ok
            success = (r.status_code == requests.codes.ok)
        else:
            # if this datastream does not yet exist, add it
            r = self.obj.api.addDatastream(self.obj.pid, self.id,
                    controlGroup=self.defaults['control_group'],
                    logMessage=logmessage, **save_opts)
            # expects 201 created
            success = (r.status_code == requests.codes.created)
            # clean-up required for object info after adding a new datastream
            if success:
                # update exists flag - if add succeeded, the datastream exists now
                self.exists = True
                # if the datastream content is a file-like object, clear it out
                # (we don't want to attempt to save the current file contents again,
                # particularly since the file is not guaranteed to still be open)
                if 'content' in save_opts and hasattr(save_opts['content'], 'read'):
                    self._content = None
                    self._content_modified = False

        if success:
            # update modification indicators
            self.info_modified = False
            self.checksum_modified = False
            self.digest = self._content_digest()
            # clear out ds location
            self.ds_location = None

        return success