Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

#!/usr/bin/env python 

 

""" 

@file ion/services/coi/resource_registry/association_client.py 

@author David Stuebe 

@brief Association Client and Association Instance are manager abstractions for associations 

 

@ TODO 

""" 

 

from twisted.internet import defer 

 

import ion.util.ionlog 

 

log = ion.util.ionlog.getLogger(__name__) 

 

from ion.core import ioninit 

 

from ion.core.process import process 

from ion.core.object import workbench, repository 

from ion.core.object.association_manager import AssociationInstance, AssociationManager 

 

from ion.services.dm.inventory.association_service import AssociationServiceClient, ASSOCIATION_QUERY_MSG_TYPE 

 

from google.protobuf import message 

from google.protobuf.internal import containers 

from ion.core.object import object_utils 

 

 

RESOURCE_DESCRIPTION_TYPE = object_utils.create_type_identifier(object_id=1101, version=1) 

RESOURCE_TYPE = object_utils.create_type_identifier(object_id=1102, version=1) 

IDREF_TYPE = object_utils.create_type_identifier(object_id=4, version=1) 

 

CONF = ioninit.config(__name__) 

 

class AssociationClientError(Exception): 

    """ 

    A class for association client exceptions 

    """ 

 

 

class AssociationClient(object): 

    """ 

    @brief This is the base class for a resource client. It is a factory for resource 

    instances. The resource instance provides the interface for working with resources. 

    The client helps create and manage resource instances. 

    """ 

 

    def __init__(self, proc=None, datastore_service='datastore'): 

        """ 

        Initializes a association client 

        @param proc a IProcess instance as originator of messages 

        @param datastore the name of the datastore service with which you wish to 

        interact with the OOICI. 

        """ 

        if not proc: 

            proc = process.Process() 

 

        self.proc = proc 

 

        # The resource client is backed by a process workbench. 

        self.workbench = self.proc.workbench 

 

        self.datastore_service = datastore_service 

 

        self.asc = AssociationServiceClient(proc=self.proc) 

 

 

 

    @defer.inlineCallbacks 

    def _check_init(self): 

        """ 

        Called in client methods to ensure that there exists a spawned process 

        to send and receive messages 

        """ 

        if not self.proc.is_spawned(): 

            yield self.proc.spawn() 

 

        assert isinstance(self.workbench, workbench.WorkBench),\ 

        'Process workbench is not initialized' 

 

 

 

 

    @defer.inlineCallbacks 

    def create_association(self, subject, predicate_or_id, obj): 

        """ 

        @Brief Create an association between two resource instances 

        @param subject is a resource instance which is to be the subject of the association 

        @param predicate_id is the predicate id to use in creating the association 

        @param obj is a resource instance which is to be the object of the association 

        """ 

        yield self._check_init() 

 

        #if not isinstance(ResourceInstance, subject): 

        #    raise TypeError('The subject argument in the resource client, create_association method must be a resource instance.') 

        # 

        #if not isinstance(ResourceInstance, obj): 

        #    raise TypeError('The obj argument in the resource client, create_association method must be a resource instance.') 

 

        if hasattr(predicate_or_id, 'Repository'): 

            predicate_repo = predicate_or_id 

        elif isinstance(predicate_or_id, (str, unicode)): 

            predicate_repo = self.workbench.get_repository(predicate_or_id) 

        else: 

            log.error('AssociationClient Error: type - %s, value - %s', type(predicate_or_id),str(predicate_or_id)) 

            raise AssociationClientError('Invalid predicate_or_id passed to Create Association. Only a string ID or Predicate Repository can be passed.') 

 

 

        if predicate_repo is None: 

            yield self.workbench.pull(self.datastore_service, predicate_or_id) 

            predicate_repo = self.workbench.get_repository(predicate_or_id) 

        yield predicate_repo.checkout('master') 

 

        # Commit the current state of the subject and object 

        if not hasattr(subject, 'Repository'): 

            log.error('AssociationClient Error: type - %s, value - %s', type(subject),str(subject)) 

            raise AssociationClientError('Invalid subject passed to Create Association. Only Object Repositories and Instance types can be passed as subject or object') 

 

        if not hasattr(obj, 'Repository'): 

            log.error('AssociationClient Error: type - %s, value - %s', type(obj),str(obj)) 

            raise AssociationClientError('Invalid object passed to Create Association. Only Object Repositories and Instance types can be passed as subject or object') 

 

        if subject.Repository.status == subject.Repository.MODIFIED: 

            subject.Repository.commit('Committing subject repository before association.') 

 

        if obj.Repository.status == obj.Repository.MODIFIED: 

            obj.Repository.commit('Committing object repository before association.') 

 

        # The workbench method returns a fully formed association instance! 

        association = self.workbench.create_association(subject, predicate_repo, obj) 

 

        defer.returnValue(association) 

 

    @defer.inlineCallbacks 

    def get_instance(self, association_id): 

        """ 

        @brief Get the latest version of the identified association from the data store 

        @param association_id can be either a string association identity or an IDRef 

        object which specifies the association identity as well as optional parameters 

        version and version state. 

        @retval the specified AssociationInstance 

 

        """ 

        yield self._check_init() 

 

        reference = None 

        branch = 'master' 

        commit = None 

 

        # Get the type of the argument and act accordingly 

        if hasattr(association_id, 'ObjectType') and association_id.ObjectType == IDREF_TYPE: 

            # If it is a resource reference, unpack it. 

            if association_id.branch: 

                branch = association_id.branch 

 

            reference = association_id.key 

            commit = association_id.commit 

 

        elif isinstance(association_id, (str, unicode)): 

            # if it is a string, us it as an identity 

            reference = association_id 

            # @TODO Some reasonable test to make sure it is valid? 

 

        else: 

            raise AssociationClientError('''Illegal argument type in get_instance: 

                                      \n type: %s \nvalue: %s''' % (type(association_id), str(association_id))) 

 

            # Pull the repository 

        try: 

            result = yield self.workbench.pull(self.datastore_service, reference) 

        except workbench.WorkBenchError, ex: 

            log.warn(ex) 

            raise AssociationClientError('Could not pull the requested association from the datastore. Workbench exception: \n %s' % ex) 

 

        # Get the repository 

        repo = self.workbench.get_repository(reference) 

        try: 

            yield repo.checkout(branch) 

        except repository.RepositoryError, ex: 

            log.warn('Could not check out branch "%s":\n Current repo state:\n %s' % (branch, str(repo))) 

            raise AssociationClientError('Could not checkout branch during get_instance.') 

 

        # Create a association instance to return 

        # @TODO - Check and see if there is already one - what to do? 

        association = AssociationInstance(repo, self.workbench) 

 

        defer.returnValue(association) 

 

    @defer.inlineCallbacks 

    def association_exists(self, subject_or_id, predicate_or_id, object_or_id): 

        """ 

        @Brief Test for the existence of an association between these three resource or object identities 

        @TODO change to take either string or IDref  

        """ 

 

        request = yield self.proc.message_client.create_instance(ASSOCIATION_QUERY_MSG_TYPE) 

 

        request.object = request.CreateObject(IDREF_TYPE) 

        if isinstance(object_or_id, (str, unicode)): 

            request.object.key = object_or_id 

        elif hasattr(object_or_id, 'Repository'): 

            object_or_id.Repository.set_repository_reference(request.object, current_state=True) 

 

        request.predicate = request.CreateObject(IDREF_TYPE) 

        if isinstance(predicate_or_id, (str, unicode)): 

            request.predicate.key = predicate_or_id 

        elif hasattr(predicate_or_id, 'Repository'): 

            predicate_or_id.Repository.set_repository_reference(request.predicate, current_state=True) 

 

 

        request.subject = request.CreateObject(IDREF_TYPE) 

        if isinstance(subject_or_id, (str, unicode)): 

            request.subject.key = subject_or_id 

        elif hasattr(subject_or_id, 'Repository'): 

            subject_or_id.Repository.set_repository_reference(request.subject, current_state=True) 

 

        result = yield self.asc.association_exists(request) 

 

        defer.returnValue(result.result) 

 

 

    @defer.inlineCallbacks 

    def find_associations(self, subject=None, predicate_or_predicates=None, obj=None): 

        """ 

        @Brief Get associations to a subject and/or object. Specify a predicate or predicates to limit the results 

        @retval An association manager instance which can be used to iterate or and sort results 

        """ 

 

        predicates = predicate_or_predicates 

        if predicates is None: 

            predicates = [None] 

 

        elif isinstance(predicate_or_predicates, (str, unicode)): 

            predicates = [predicate_or_predicates] 

        elif hasattr(predicate_or_predicates, 'Repository'): 

            predicates = [predicate_or_predicates] 

        elif isinstance(predicate_or_predicates, list): 

 

            if None in predicates: 

                raise AssociationClientError('None can not be in the list of predicates passed to find_associations') 

 

            # Do other type checking on the list later 

        else: 

            raise AssociationClientError('Invalid argument type for predicate passed to find_associations') 

 

 

        if subject is None and obj is None: 

            raise AssociationClientError('Either the subject and/or the obj must be specified in find_associations') 

 

 

        if subject is not None and not hasattr(subject,  'Repository'): 

            raise AssociationClientError('The subject argument in the resource client, fidn_associations method must be a resource instance.') 

 

        if obj is not None and not hasattr(obj,  'Repository'): 

            raise AssociationClientError('The "obj" argument in the resource client, find_associations method must be a resource instance.') 

 

 

        def_list = [] 

 

        for predicate in predicates: 

 

            request = yield self.proc.message_client.create_instance(ASSOCIATION_QUERY_MSG_TYPE) 

 

            if obj is not None: 

                request.object = request.CreateObject(IDREF_TYPE) 

                request.object.key = obj.Repository.repository_key 

 

            if predicate is not None: 

                request.predicate = request.CreateObject(IDREF_TYPE) 

 

                if isinstance(predicate, (str, unicode)): 

                    request.predicate.key  = predicate 

                elif hasattr(predicate, 'Repository'): 

                    request.predicate.key = predicate.Repository.repository_key 

                else: 

                    raise AssociationClientError('None can not be in the list of predicates passed to find_associations') 

 

            if subject is not None: 

                request.subject = request.CreateObject(IDREF_TYPE) 

                request.subject.key = subject.Repository.repository_key 

 

            def_list.append(self.asc.get_associations(request)) 

 

 

        result_list = yield defer.DeferredList(def_list) 

 

        association_manager = AssociationManager() 

        for result, assoc_ref_list in result_list: 

 

            for assoc_ref in assoc_ref_list.idrefs: 

 

                yield self.workbench.pull(self.datastore_service, assoc_ref.key) 

                assoc = self.workbench.get_repository(assoc_ref.key) 

                assoc.checkout(assoc_ref.branch) 

 

                association = AssociationInstance(assoc, self.workbench) 

 

                association_manager.add(association) 

 

        defer.returnValue(association_manager)