activerdf_sesame 0.1

Sign up to get free protection for your applications and to get access to all the features.
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