activerdf_sesame 0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/LICENSE ADDED
File without changes
data/README ADDED
File without changes
data/Rakefile ADDED
@@ -0,0 +1,55 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/clean'
4
+ require 'rake/gempackagetask'
5
+ require 'rake/rdoctask'
6
+ require '../tools/rakehelp'
7
+ require 'fileutils'
8
+ include FileUtils
9
+
10
+ setup_tests
11
+ setup_rdoc ['README', 'LICENSE', 'lib/**/*.rb', 'doc/**/*.rdoc']
12
+
13
+ desc "test and package gem"
14
+ task :default => [:test, :package]
15
+
16
+ # get @VERSION from commandline
17
+ @VERSION = '0.1'
18
+ NAME="activerdf_sesame"
19
+ GEMNAME="#{NAME}-#{@VERSION}.gem"
20
+
21
+ # which files should go into the gem?
22
+ PKG_FILES = FileList[
23
+ 'ext/*.jar',
24
+ '[A-Z]*',
25
+ 'lib/**/*.rb',
26
+ 'test/**/*',
27
+ ]
28
+
29
+ # define package task
30
+ setup_gem(NAME,@VERSION) do |spec|
31
+ spec.summary = "an RDF database for usage in ActiveRDF (based on sesame2)"
32
+ spec.description = spec.summary
33
+ spec.author="Benjamin Heitmann <benjamin.heitmann@deri.org>"
34
+ spec.add_dependency('gem_plugin', '>= 0.2.1')
35
+ spec.add_dependency('activerdf', '>= 1.2')
36
+ spec.files = PKG_FILES.to_a
37
+
38
+ end
39
+
40
+
41
+
42
+ task :install => [:package] do
43
+ sh %{sudo gem install pkg/#{NAME}-#{@VERSION}.gem}
44
+ end
45
+
46
+ task :uninstall => [:clean] do
47
+ sh %{sudo gem uninstall #{NAME}}
48
+ end
49
+
50
+ task :reinstall => [:uninstall, :install]
51
+
52
+ task :upload => :package do |task|
53
+ sh "scp pkg/#{GEMNAME} eyal@activerdf.org:/home/eyal/webs/activerdf/gems/"
54
+ end
55
+
Binary file
@@ -0,0 +1,11 @@
1
+
2
+ # add the directory in which this file is located to the ruby loadpath
3
+ file =
4
+ if File.symlink?(__FILE__)
5
+ File.readlink(__FILE__)
6
+ else
7
+ __FILE__
8
+ end
9
+ $: << File.dirname(File.expand_path(file))
10
+
11
+ require 'sesame'
@@ -0,0 +1,312 @@
1
+ # Author:: Eyal Oren
2
+ # Copyright:: (c) 2005-2006 Eyal Oren
3
+ # License:: LGPL
4
+
5
+ require 'active_rdf'
6
+ require 'federation/connection_pool'
7
+
8
+ $activerdflog.info "loading Sesame adapter"
9
+
10
+
11
+ # ----- java imports and extentsions
12
+ require 'java'
13
+
14
+ StringWriter = java.io.StringWriter
15
+ FileReader = java.io.FileReader
16
+ JFile = java.io.File
17
+ URLClassLoader = java.net.URLClassLoader
18
+ JURL = java.net.URL
19
+ JClass = java.lang.Class
20
+ JObject = java.lang.Object
21
+
22
+ # sesame specific classes:
23
+ WrapperForSesame2 = org.activerdf.wrapper.sesame2.WrapperForSesame2
24
+ QueryLanguage = org.openrdf.querymodel.QueryLanguage
25
+ NTriplesWriter = org.openrdf.rio.ntriples.NTriplesWriter
26
+ RDFFormat = org.openrdf.rio.RDFFormat
27
+
28
+
29
+
30
+ # TODO: about this adapter
31
+ class SesameAdapter < ActiveRdfAdapter
32
+ ConnectionPool.register_adapter(:sesame,self)
33
+
34
+ # instantiates Sesame database
35
+ # available parameters:
36
+ # * :location => path to a file for persistent storing or :memory for in-memory (defaults to in-memory)
37
+ # * :inferencing => true or false, if sesame2 rdfs inferencing is uses (defaults to true)
38
+ def initialize(params = {})
39
+ $activerdflog.info "initializing Sesame Adapter with params #{params.to_s}"
40
+
41
+ @reads = true
42
+ @writes = true
43
+
44
+ # if no directory path given, we use in-memory store
45
+ if params[:location]
46
+ if params[:location] == :memory
47
+ sesameLocation = nil
48
+ else
49
+ sesameLocation = JFile.new(params[:location])
50
+ end
51
+ else
52
+ sesameLocation = nil
53
+ end
54
+
55
+ # if no inferencing is specified, we use the sesame2 rdfs inferencing
56
+ sesameInferencing = params[:inferencing] || nil
57
+
58
+ # this will not work at the current state of jruby
59
+ # # fancy JRuby code so that the user does not have to set the java CLASSPATH
60
+ #
61
+ # this_dir = File.dirname(File.expand_path(__FILE__))
62
+ #
63
+ # jar1 = JFile.new(this_dir + "/../../ext/wrapper-sesame2.jar")
64
+ # jar2 = JFile.new(this_dir + "/../../ext/openrdf-sesame-2.0-alpha4-onejar.jar")
65
+ #
66
+ # # make an array of URL, which contains the URLs corresponding to the files
67
+ # uris = JURL[].new(2)
68
+ # uris[0] = jar1.toURL
69
+ # uris[1] = jar2.toURL
70
+ #
71
+ # # this is our custom class loader, yay!
72
+ # @activerdfClassLoader = URLClassLoader.new(uris)
73
+ # classWrapper = JClass.forName("org.activerdf.wrapper.sesame2.WrapperForSesame2", true, @activerdfClassLoader)
74
+ # @myWrapperInstance = classWrapper.new_instance
75
+
76
+ @myWrapperInstance = WrapperForSesame2.new
77
+
78
+ if sesameLocation == nil
79
+ if sesameInferencing == nil
80
+ @db = @myWrapperInstance.callConstructor
81
+ else
82
+ @db = @myWrapperInstance.callConstructor(sesameInferencing)
83
+ end
84
+ else
85
+ if sesameInferencing == nil
86
+ @db = @myWrapperInstance.callConstructor(sesameLocation)
87
+ else
88
+ @db = @myWrapperInstance.callConstructor(sesameLocation,sesameInferencing)
89
+ end
90
+ end
91
+
92
+ @valueFactory = @db.getRepository.getSail.getValueFactory
93
+
94
+ # define the finalizer, which will call close on the sesame triple store
95
+ # recipie for this, is from: http://wiki.rubygarden.org/Ruby/page/show/GCAndMemoryManagement
96
+ ObjectSpace.define_finalizer(self, SesameAdapter.create_finalizer(@db))
97
+ end
98
+
99
+ # TODO: this does not work, but it is also not caused by jruby.
100
+ def SesameAdapter.create_finalizer(db)
101
+ # we have to call close on the sesame triple store, because otherwise some of the iterators are not closed properly
102
+ proc { puts "die"; db.close }
103
+ end
104
+
105
+
106
+
107
+ # returns the number of triples in the datastore (incl. possible duplicates)
108
+ def size
109
+ @db.size
110
+ end
111
+
112
+ # deletes all triples from datastore
113
+ def clear
114
+ @db.clear
115
+ end
116
+
117
+ # deletes triple(s,p,o,c) from datastore
118
+ # symbol parameters match anything: delete(:s,:p,:o) will delete all triples
119
+ # you can specify a context to limit deletion to that context:
120
+ # delete(:s,:p,:o, 'http://context') will delete all triples with that context
121
+ def delete(s, p, o, c=nil)
122
+ if s.class == RDFS::Resource then
123
+ sesameSubject = @valueFactory.createURI(s.uri)
124
+ elsif s == :s
125
+ sesameSubject = nil
126
+ else
127
+ raise ActiveRdfError, "the Sesame Adapter tried to delete a subject which was not of type RDFS::Resource, but of type #{s.class}"
128
+ end
129
+ if p.class == RDFS::Resource then
130
+ sesamePredicate = @valueFactory.createURI(p.uri)
131
+ elsif p == :p
132
+ sesamePredicate = nil
133
+ else
134
+ raise ActiveRdfError, "the Sesame Adapter tried to delete a predicate which was not of type RDFS::Resource, but of type #{p.class}"
135
+ end
136
+ if o.class == RDFS::Resource then
137
+ sesameObject = @valueFactory.createURI(o.uri)
138
+ elsif o == :o
139
+ sesameObject = nil
140
+ else
141
+ sesameObject = @valueFactory.createLiteral(o.to_s)
142
+ end
143
+
144
+ # TODO contexts
145
+ candidateStatements = @db.getStatements(sesameSubject, sesamePredicate, sesameObject, false)
146
+
147
+ @db.remove(candidateStatements)
148
+
149
+ candidateStatements.close
150
+ return @db
151
+ end
152
+
153
+ # adds triple(s,p,o) to datastore
154
+ # s,p must be resources, o can be primitive data or resource
155
+ def add(s,p,o,c=nil)
156
+
157
+ if s.class == RDFS::Resource then
158
+ sesameSubject = @valueFactory.createURI(s.uri)
159
+ else
160
+ raise ActiveRdfError, "the Sesame Adapter tried to add a subject which was not of type RDFS::Resource, but of type #{s.class}"
161
+ end
162
+ if p.class == RDFS::Resource then
163
+ sesamePredicate = @valueFactory.createURI(p.uri)
164
+ else
165
+ raise ActiveRdfError, "the Sesame Adapter tried to add a predicate which was not of type RDFS::Resource, but of type #{p.class}"
166
+ end
167
+ if o.class == RDFS::Resource then
168
+ sesameObject = @valueFactory.createURI(o.uri)
169
+ else
170
+ sesameObject = @valueFactory.createLiteral(o.to_s)
171
+ end
172
+
173
+ # TODO: handle context, especially if it is null
174
+
175
+ @db.add(sesameSubject, sesamePredicate, sesameObject)
176
+ # for contexts, just add 4th parameter
177
+
178
+ # TODO: do we need to handle errors from the java side ?
179
+
180
+ return @db
181
+ end
182
+
183
+
184
+
185
+ # flushing is done automatically, because we run sesame2 in autocommit mode
186
+ def flush
187
+ true
188
+ end
189
+ # saving is done automatically, because we run sesame2 in autocommit mode
190
+ def save
191
+ true
192
+ end
193
+
194
+ # close the underlying sesame triple store.
195
+ # if not called there may be open iterators.
196
+ def close
197
+ @db.close
198
+ end
199
+
200
+ # returns all triples in the datastore
201
+ def dump
202
+ # the sesame connection has an export method, which writes all explicit statements to
203
+ # a to a RDFHandler, which we supply, by constructing a NTriplesWriter, which writes to StringWriter,
204
+ # and we kindly ask that StringWriter to make a string for us. Note, you have to use stringy.to_s,
205
+ # somehow stringy.toString does not work. yes yes, those wacky jruby guys ;)
206
+ stringy = StringWriter.new
207
+ sesameWriter = NTriplesWriter.new(stringy)
208
+ @db.export(sesameWriter)
209
+ return stringy.to_s
210
+ end
211
+
212
+ # loads triples from file in ntriples format
213
+ def load(file)
214
+ reader = FileReader.new(file)
215
+ @db.add(reader, "", RDFFormat::NTRIPLES)
216
+
217
+ return @db
218
+ end
219
+
220
+ # executes ActiveRDF query on the sesame triple store associated with this adapter
221
+ def query(query)
222
+
223
+ # we want to put the results in here
224
+ results = []
225
+
226
+ # translate the query object into a SPARQL query string
227
+ qs = Query2SPARQL.translate(query)
228
+
229
+ # evaluate the query on the sesame triple store
230
+ # TODO: if we want to get inferred statements back we have to say so, as third boolean parameter
231
+ tuplequeryresult = @db.evaluateTupleQuery(QueryLanguage::SPARQL, qs)
232
+
233
+ # what are the variables of the query ?
234
+ variables = tuplequeryresult.getBindingNames
235
+ sizeOfVariables = variables.size
236
+
237
+ # a solution is a binding of a variable to all entities that matched this variable in the sparql query
238
+ solutionIterator = tuplequeryresult.iterator
239
+
240
+ # the following is plainly ugly. the reason is that JRuby currently does not support
241
+ # using iterators in the ruby way: with "each". it is possible to define "each" for java.util.Iterator
242
+ # using JavaUtilities.extend_proxy but that fails in strange ways. this is ugly but works.
243
+
244
+ # TODO: null handling, if a value is null...
245
+
246
+ # if there only was one variable, then the results array should look like this:
247
+ # results = [ [first Value For The Variable], [second Value], ...]
248
+ if sizeOfVariables == 1 then
249
+ # the counter keeps track of the number of values, so we can insert them into the results at the right position
250
+ counter = 0
251
+ while solutionIterator.hasNext
252
+ solution = solutionIterator.next
253
+
254
+ temparray = []
255
+ # get the value associated with a variable in this specific solution
256
+ temparray[0] = convertSesame2ActiveRDF(solution.getValue(variables[0]))
257
+ results[counter] = temparray
258
+ counter = counter + 1
259
+ end
260
+ else
261
+ # if there is more then one variable the results array looks like this:
262
+ # results = [ [Value From First Solution For First Variable, Value From First Solution For Second Variable, ...],
263
+ # [Value From Second Solution For First Variable, Value From Second Solution for Second Variable, ...], ...]
264
+ counter = 0
265
+ while solutionIterator.hasNext
266
+ solution = solutionIterator.next
267
+
268
+ temparray = []
269
+ for n in 1..sizeOfVariables
270
+ value = convertSesame2ActiveRDF(solution.getValue(variables[n-1]))
271
+ temparray[n-1] = value
272
+ end
273
+ results[counter] = temparray
274
+ counter = counter + 1
275
+ end
276
+ end
277
+
278
+ return results
279
+ end
280
+
281
+ private
282
+
283
+ # check if testee is a java subclass of reference
284
+ def jInstanceOf(testee, reference)
285
+ # for Java::JavaClass for a <=> b the comparison operator returns: -1 if a is subclass of b,
286
+ # 0 if a.jclass = b.jclass, +1 in any other case.
287
+ isSubclass = (testee <=> reference)
288
+ if isSubclass == -1 or isSubclass == 0
289
+ return true
290
+ else
291
+ return false
292
+ end
293
+ end
294
+
295
+ # takes a part of a sesame statement, and converts it to a RDFS::Resource if it is a URI,
296
+ # or to a String if it is a Literal. The assumption currently, is that we will only get stuff out of sesame,
297
+ # which we put in there ourselves, and currently we only put URIs or Literals there.
298
+ # TODO: do we need to think about handling blank nodes ? e.g. if the are part of a graph read from a file ?
299
+ def convertSesame2ActiveRDF(input)
300
+ jclassURI = Java::JavaClass.for_name("org.openrdf.model.URI")
301
+ jclassLiteral = Java::JavaClass.for_name("org.openrdf.model.Literal")
302
+
303
+ if jInstanceOf(input.java_class, jclassURI)
304
+ return RDFS::Resource.new(input.toString)
305
+ elsif jInstanceOf(input.java_class, jclassLiteral)
306
+ return input.toString
307
+ else
308
+ raise ActiveRdfError, "the Sesame Adapter tried to return something which is neither a URI nor a Literal, but is instead a #{input.java_class.name}"
309
+ end
310
+ end
311
+
312
+ end