activerdf_jena 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/README ADDED
@@ -0,0 +1,47 @@
1
+ This is the ActiveRDF adapter to the Jena RDF library.
2
+
3
+ Features:
4
+ * supports memory, file and database stores; database
5
+ stores can be configured both with datasources and raw connection
6
+ parameters. Jena currently supports Oracle, MySQL, HSQLDB,
7
+ PostgreSQL, MS SQL, and Derby.
8
+ * supports reasoners, including Pellet and the built-in Jena reasoners
9
+ * supports Lucene query support in ARQ
10
+ (since LARQ doesn't allow you to add statements
11
+ and reindex only those statements, whenever we've add a statement to a
12
+ triple store, we must rebuild the index at query time)
13
+
14
+ Requirements:
15
+ * JRuby is required to natively execute Jena
16
+ * the application can only use pure ruby gems
17
+
18
+ License:
19
+ included LGPL license (version 2 or later).
20
+
21
+
22
+ ------------
23
+
24
+ Installing and running JRuby:
25
+ =============================
26
+
27
+ Download the newest JRuby distribution from http://jruby.codehaus.org/
28
+
29
+ Unpack it somewhere, e.g. /usr/local/jruby
30
+
31
+ Then set your environement:
32
+
33
+ export JRUBY_HOME=/usr/local/jruby
34
+ export JAVA_HOM=/path/to/java/home
35
+ export PATH=$JRUBY_HOME/bin:$PATH
36
+
37
+ check if you now have the correct jruby commands in your path:
38
+ which jruby -> /usr/local/jruby/bin/jruby
39
+ which gem -> /usr/local/jruby/bin/gem
40
+
41
+ Now you can install rails and activerdf:
42
+ gem install rails --include-dependencies --no-rdoc --no-ri
43
+ gem install activerdf --include-dependencies --no-rdoc --no-ri
44
+ gem install activerdf_jena --include-dependencies --no-rdoc --no-ri
45
+
46
+ For more information see
47
+ http://wiki.activerdf.org/GettingStartedGuide
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,24 @@
1
+ #
2
+ # Author: Karsten Huneycutt
3
+ # Copyright 2007 Valkeir Corporation
4
+ # License: LGPL
5
+ #
6
+ # add the directory in which this file is located to the ruby loadpath
7
+ file =
8
+ if File.symlink?(__FILE__)
9
+ File.readlink(__FILE__)
10
+ else
11
+ __FILE__
12
+ end
13
+ $: << File.dirname(File.expand_path(file))
14
+
15
+ java_dir = File.expand_path(File.join(File.dirname(File.expand_path(file)), "..", "..", "ext"))
16
+
17
+ Dir.foreach(java_dir) do |jar|
18
+ $CLASSPATH << File.join(java_dir, jar) if jar =~ /.jar$/
19
+ end
20
+
21
+ require 'jena'
22
+ require 'pellet'
23
+ require 'lucene'
24
+ require 'jena_adapter'
@@ -0,0 +1,59 @@
1
+ #
2
+ # Author: Karsten Huneycutt
3
+ # Copyright 2007 Valkeir Corporation
4
+ # License: LGPL
5
+ #
6
+ require 'java'
7
+
8
+ module Jena
9
+
10
+ module Ontology
11
+ include_package('com.hp.hpl.jena.ontology')
12
+ end
13
+
14
+ module Model
15
+ include_package('com.hp.hpl.jena.rdf.model')
16
+ end
17
+
18
+ module DB
19
+ include_package('com.hp.hpl.jena.db')
20
+
21
+ # this maps downcased Jena database types into drivers
22
+ DRIVER_MAP = {
23
+ 'oracle' => 'oracle.jdbc.Driver',
24
+ 'mysql' => 'com.mysql.jdbc.Driver',
25
+ 'derby' => 'org.apache.derby.jdbc.EmbeddedDriver',
26
+ 'postgresql' => 'org.postgresql.Driver',
27
+ 'hsql' => 'org.hsqldb.jdbcDriver',
28
+ 'mssql' => 'com.microsoft.sqlserver.jdbc.SQLServerDriver'
29
+ }
30
+
31
+ DRIVER_MAP.each do |name, driver|
32
+ av = "#{name}_available"
33
+ (class << self ; self ; end).send(:bool_accessor, av.to_sym)
34
+ begin
35
+ java.lang.Class.forName driver
36
+ Jena::DB.send("#{av}=", true)
37
+ rescue
38
+ Jena::DB.send("#{av}=", false)
39
+ end
40
+ end
41
+ end
42
+
43
+ module Query
44
+ include_package('com.hp.hpl.jena.query')
45
+ end
46
+
47
+ module Reasoner
48
+ include_package('com.hp.hpl.jena.reasoner')
49
+ end
50
+
51
+ module Datatypes
52
+ include_package('com.hp.hpl.jena.datatypes')
53
+ end
54
+
55
+ module Graph
56
+ include_package('com.hp.hpl.jena.graph')
57
+ end
58
+
59
+ end
@@ -0,0 +1,480 @@
1
+ #
2
+ # Author: Karsten Huneycutt
3
+ # Copyright 2007 Valkeir Corporation
4
+ # License: LGPL
5
+ #
6
+
7
+ class JenaAdapter < ActiveRdfAdapter
8
+
9
+ class JenaAdapterConfigurationError < StandardError
10
+ end
11
+
12
+ class DataSourceDBConnection < Jena::DB::DBConnection
13
+
14
+ attr_accessor :datasource, :connection
15
+
16
+ def initialize(datasource, type)
17
+ if datasource.kind_of? javax.sql.DataSource
18
+ self.datasource = datasource
19
+ else
20
+ self.datasource = javax.naming.InitialContext.new.lookup(datasource)
21
+ end
22
+ self.setDatabaseType(type)
23
+ end
24
+
25
+ def getConnection
26
+ if !self.connection || !valid_connection?(self.connection)
27
+ self.connection = self.datasource.getConnection
28
+ end
29
+ self.connection
30
+ end
31
+
32
+ def close
33
+ self.datasource = nil
34
+ end
35
+
36
+ def valid_connection?(cnxn)
37
+ true
38
+ end
39
+
40
+ end
41
+
42
+ ConnectionPool.register_adapter(:jena, self)
43
+
44
+ bool_accessor :keyword_search, :reasoning
45
+ bool_accessor :lucene_index_behind
46
+ attr_accessor :ontology_type, :model_name, :reasoner, :connection
47
+ attr_accessor :model_maker, :base_model, :model, :lucene_index
48
+ attr_accessor :root_directory
49
+
50
+ # :database
51
+ # either use :url, :type, :username, AND :password (for a
52
+ # regular connection) OR :datasource AND :type (for a container
53
+ # connection), default to memory data store
54
+ # example for a derby connection:
55
+ # :database => {:url => "jdbc:derby:superfunky;create=true", :type => "Derby", :username => "", :password => ""}
56
+ # :file
57
+ # database wins over this, this wins over memory store. parameter is
58
+ # a string or file indicating the root directory for all files.
59
+ # :model
60
+ # name of model to use, default is jena's default
61
+ # :ontology
62
+ # set to language type if this needs to be viewed as an ontology,
63
+ # default nil, available :owl, :owl_dl, :owl_lite, :rdfs
64
+ # :reasoner
65
+ # set to reasoner to use -- default nil (none). options: :pellet,
66
+ # :transitive, :rdfs, :rdfs_simple, :owl_micro, :owl_mini, :owl,
67
+ # :generic_rule
68
+ # :lucene
69
+ # set to true to enable true lucene indexing of this store, default false
70
+ def initialize(params = {})
71
+ dbparams = params[:database]
72
+ self.ontology_type = params[:ontology]
73
+ self.reasoner = params[:reasoner]
74
+ self.keyword_search = params[:lucene]
75
+
76
+ # if the model name is not provided and file persistence is used, then jena just
77
+ # creates random files in the tmp dir. not good, as we need to know the model name
78
+ # to have persistence
79
+ if params[:model]
80
+ self.model_name = params[:model]
81
+ else
82
+ self.model_name = "default"
83
+ end
84
+
85
+ if params[:file]
86
+ if params[:file].respond_to? :path
87
+ self.root_directory = File.expand_path(params[:file].path)
88
+ else
89
+ self.root_directory = params[:file]
90
+ end
91
+ end
92
+
93
+ # do some sanity checking
94
+ if self.keyword_search? && !LuceneARQ.lucene_available?
95
+ raise JenaAdapterConfigurationError, "Lucene requested but is not available"
96
+ end
97
+
98
+ if self.reasoner == :pellet && !Pellet.pellet_available?
99
+ raise JenaAdapterConfigurationError, "Pellet requested but not available"
100
+ end
101
+
102
+ if self.reasoner && !self.ontology_type
103
+ raise JenaAdapterConfigurationError, "Ontology model needed for reasoner"
104
+ end
105
+
106
+ if dbparams
107
+ if dbparams[:datasource]
108
+ self.connection = DataSourceDBConnection.new(dbparams[:datasource],
109
+ dbparams[:type])
110
+ else
111
+ begin
112
+ if !Jena::DB.send("#{dbparams[:type].downcase}_available?")
113
+ raise JenaAdapterConfigurationError, "database type #{dbparams[:type]} not available"
114
+ end
115
+ rescue NameError
116
+ raise JenaAdapterConfigurationError, "database type #{dbparams[:type]} not recognized"
117
+ end
118
+
119
+ self.connection = Jena::DB::DBConnection.new(dbparams[:url],
120
+ dbparams[:username],
121
+ dbparams[:password],
122
+ dbparams[:type])
123
+ end
124
+
125
+ self.model_maker = Jena::Model::ModelFactory.createModelRDBMaker(connection)
126
+
127
+ elsif self.root_directory
128
+ self.model_maker = Jena::Model::ModelFactory.createFileModelMaker(self.root_directory)
129
+ else
130
+ self.model_maker = Jena::Model::ModelFactory.createMemModelMaker
131
+ end
132
+
133
+
134
+ self.base_model = self.model_maker.openModel(model_name)
135
+
136
+ if self.ontology_type
137
+ rf = map_reasoner_factory(self.reasoner)
138
+ onturi = map_ontology_type(self.ontology_type)
139
+
140
+ spec =
141
+ Jena::Ontology::OntModelSpec.new(self.model_maker,
142
+ Jena::Ontology::OntDocumentManager.new,
143
+ rf, onturi)
144
+
145
+ self.model = Jena::Model::ModelFactory.
146
+ createOntologyModel(spec, self.base_model)
147
+ self.reasoning = true
148
+ else
149
+ self.model = self.base_model
150
+ self.reasoning = false
151
+ end
152
+
153
+ self.reads = true
154
+ self.writes = true
155
+
156
+ self
157
+ end
158
+
159
+
160
+ def size
161
+ self.model.size
162
+ end
163
+
164
+ def dump
165
+ it = self.model.listStatements
166
+ res = ""
167
+ while it.hasNext
168
+ res += it.nextStatement.asTriple.toString
169
+ res += " . \n"
170
+ end
171
+ res
172
+ end
173
+
174
+ def close
175
+ ConnectionPool.remove_data_source(self)
176
+ self.model.close
177
+ self.connection.close unless self.connection.nil?
178
+ end
179
+
180
+ def clear
181
+ self.model.removeAll
182
+ self.model.prepare if self.model.respond_to? :prepare
183
+ self.model.rebind if self.model.respond_to? :rebind
184
+ end
185
+
186
+
187
+ def delete(subject, predicate, object, context = nil)
188
+ self.lucene_index_behind = true
189
+ s = (is_wildcard?(subject) ? nil : build_subject(subject))
190
+ p = (is_wildcard?(predicate) ? nil : build_predicate(predicate))
191
+ o = (is_wildcard?(object) ? nil : build_object(object))
192
+ self.model.removeAll(s, p, o)
193
+ self.model.prepare if self.model.respond_to? :prepare
194
+ self.model.rebind if self.model.respond_to? :rebind
195
+ end
196
+
197
+ def add(subject, predicate, object, context = nil)
198
+ self.lucene_index_behind = true
199
+ self.model.add(build_statement(subject, predicate, object))
200
+ self.model.prepare if self.model.respond_to? :prepare
201
+ self.model.rebind if self.model.respond_to? :rebind
202
+ end
203
+
204
+ def flush
205
+ # no-op
206
+ end
207
+
208
+
209
+ # :format
210
+ # format -- :ntriples, :n3, or :rdfxml, default :rdfxml
211
+ # :into
212
+ # either the name of a model, :default_model for the main model, or
213
+ # :submodel to load into an anonymous memory model, default is :submodel
214
+ # if this is an ontology, :default_model if it's not.
215
+ # :rebind
216
+ # rebind with the inferencer, default true; no effect if no inferencer
217
+ def load(uri, params = {})
218
+ into = params[:into] ? params[:into] :
219
+ (self.ontology_type ? :submodel : :default_model)
220
+ format = params[:format] ? params[:format] : :rdfxml
221
+ rebind = params[:rebind] ? params[:rebind] : true
222
+
223
+ jena_format =
224
+ case format
225
+ when :rdfxml
226
+ "RDF/XML"
227
+ when :ntriples
228
+ "N-TRIPLE"
229
+ when :n3
230
+ "N3"
231
+ end
232
+
233
+ case into
234
+ when :default_model
235
+ self.model.read(uri, jena_format)
236
+
237
+ when :submodel
238
+ self.model.add(Jena::Model::ModelFactory.createDefaultModel.read(uri, jena_format))
239
+
240
+ else
241
+ self.model.add(self.model_maker.createModel(into).read(uri, jena_format))
242
+ end
243
+
244
+ if rebind && self.reasoner && self.model.respond_to?(:rebind)
245
+ self.model.rebind
246
+ end
247
+
248
+ self.lucene_index_behind = true
249
+
250
+ end
251
+
252
+ # this method gets called by the ActiveRDF query engine
253
+ def query(query, params = {})
254
+
255
+ if self.keyword_search? && query.keyword?
256
+
257
+ # duplicate the query
258
+ query_with_keywords = query.dup
259
+
260
+ # now duplicate the where stuff so we can fiddle with it...
261
+ # this is GROSS -- fix this if Query ever sprouts a proper
262
+ # deep copy or a where_clauses setter
263
+ query_with_keywords.instance_variable_set("@where_clauses", query.where_clauses.dup)
264
+
265
+ # now, for each of the keyword clauses, set up the search
266
+ query.keywords.each do |var, keyword|
267
+ # use this if activerdf expects the subject to come back and not the
268
+ # literal and using indexbuilderstring
269
+ #query.where("lucene_literal_#{var}".to_sym, LuceneARQ::KEYWORD_PREDICATE, keyword)
270
+ #query.where(var, "lucene_property_#{var}".to_sym, "lucene_literal_#{var}".to_sym)
271
+
272
+ # use this if activerdf expects the literal to come back, not the
273
+ # subject, or if using indexbuildersubject (which makes the subject
274
+ # come back instead of the literal
275
+ query_with_keywords.where(var, RDFS::Resource.new(LuceneARQ::KEYWORD_PREDICATE), keyword)
276
+
277
+ end
278
+
279
+ else
280
+ query_with_keywords = query
281
+ end
282
+
283
+ # jena knows about lucene, so use the query object that has the keyword
284
+ # search requests expanded.
285
+ jena_results = query_jena(query_with_keywords)
286
+
287
+ # use the conjunctive query facility in pellet to get additional
288
+ # answers, if we're using pellet and we don't have a pure keyword
289
+ # query
290
+ if self.reasoner == :pellet && query.where_clauses.size > 0
291
+ # pellet doesn't know about lucene, so we use the original query
292
+ # object
293
+ pellet_results = query_pellet(query)
294
+ results = (jena_results + pellet_results).uniq!
295
+ else
296
+ results = jena_results
297
+ end
298
+
299
+ if query.ask?
300
+ return [[true]] if results.size > 0
301
+ return [[false]]
302
+ end
303
+
304
+ if query.count?
305
+ return results.size
306
+ end
307
+
308
+ results
309
+
310
+ end
311
+
312
+ # ==========================================================================
313
+ # put private methods here to seperate api methods from the
314
+ # inner workings of the adapter
315
+ private
316
+
317
+ def map_ontology_type(type)
318
+ case type
319
+ when :rdfs
320
+ 'http://www.w3.org/2000/01/rdf-schema#'
321
+ when :owl
322
+ 'http://www.w3.org/2002/07/owl#'
323
+ when :owl_dl
324
+ 'http://www.w3.org/TR/owl-features/#term_OWLDL'
325
+ when :owl_lite
326
+ 'http://www.w3.org/TR/owl-features/#term_OWLLite'
327
+ else
328
+ type
329
+ end
330
+ end
331
+
332
+
333
+ def map_reasoner_factory(type)
334
+ case type
335
+ when :pellet
336
+ Pellet.reasoner_factory
337
+
338
+ when :transitive
339
+ com.hp.hpl.jena.reasoner.transitiveReasoner.TransitiveReasonerFactory.theInstance
340
+
341
+ when :rdfs
342
+ com.hp.hpl.jena.reasoner.rulesys.RDFSFBRuleReasonerFactory.theInstance
343
+
344
+ when :rdfs_simple
345
+ com.hp.hpl.jena.reasoner.rulesys.RDFSRuleReasonerFactory.theInstance
346
+
347
+ when :owl_micro
348
+ com.hp.hpl.jena.reasoner.rulesys.OWLMicroReasonerFactory.theInstance
349
+
350
+ when :owl_mini
351
+ com.hp.hpl.jena.reasoner.rulesys.OWLMiniReasonerFactory.theInstance
352
+
353
+ when :owl
354
+ com.hp.hpl.jena.reasoner.rulesys.OWLFBRuleReasonerFactory.theInstance
355
+
356
+ when :generic_rule
357
+ com.hp.hpl.jena.reasoner.rulesys.GenericRuleReasonerFactory.theInstance
358
+
359
+ else
360
+ type
361
+ end
362
+ end
363
+
364
+ def build_object(object)
365
+ if object.respond_to? :uri
366
+ o = self.model.getResource(object.uri)
367
+ else
368
+ #xlate to literal
369
+ if !object.kind_of? Literal
370
+ objlit = Literal.new object
371
+ else
372
+ objlit = object
373
+ end
374
+
375
+ if objlit.type
376
+ type = Jena::Datatypes::TypeMapper.getInstance.getTypeByName(objlit.type.uri)
377
+ o = self.model.createTypedLiteral(objlit.value, type)
378
+ elsif objlit.language
379
+ o = self.model.createLiteral(objlit.value, objlit.language)
380
+ else
381
+ o = self.model.createTypedLiteral(objlit.value, nil)
382
+ end
383
+ end
384
+ return o
385
+ end
386
+
387
+ def build_subject(subject)
388
+ self.model.getResource(subject.uri)
389
+ end
390
+
391
+ def build_predicate(predicate)
392
+ self.model.getProperty(predicate.uri)
393
+ end
394
+
395
+ def build_statement(subject, predicate, object)
396
+ s = build_subject(subject)
397
+ p = build_predicate(predicate)
398
+ o = build_object(object)
399
+ self.model.createStatement(s, p, o)
400
+ end
401
+
402
+ def is_wildcard?(thing)
403
+ (thing == nil) || thing.kind_of?(Symbol)
404
+ end
405
+
406
+
407
+ def query_jena(query)
408
+ query_sparql = translate(query)
409
+
410
+ qexec = Jena::Query::QueryExecutionFactory.create(query_sparql, self.model)
411
+
412
+ # PROBABLY A VERY EXPENSIVE OPERATION (rebuilds lucene index if ANYTHING
413
+ # changed -- this seems to be the only way, since you have to close
414
+ # the index after you build it...
415
+ if query.keyword? && self.keyword_search?
416
+ LuceneARQ::LARQ.setDefaultIndex(qexec.getContext, retrieve_lucene_index)
417
+ end
418
+
419
+ begin
420
+ results = perform_query(query, qexec)
421
+ ensure
422
+ qexec.close
423
+ end
424
+
425
+ results
426
+ end
427
+
428
+ def query_pellet(query)
429
+ query_sparql = translate(query)
430
+ jena_query = Jena::Query::QueryFactory.create(query_sparql)
431
+
432
+ # bail if not a select
433
+ return [] if !jena_query.isSelectType
434
+
435
+ qexec = Pellet::Query::PelletQueryExecution.new(jena_query, self.model)
436
+
437
+ begin
438
+ results = perform_query(query, qexec)
439
+ ensure
440
+ qexec.close
441
+ end
442
+
443
+ results
444
+ end
445
+
446
+ def perform_query(query, qexec)
447
+ results = qexec.execSelect
448
+ arr_results = []
449
+
450
+ while results.hasNext
451
+ row = results.nextSolution
452
+ res_row = []
453
+ query.select_clauses.each do |kw|
454
+ thing = row.get(kw.to_s)
455
+ if thing.kind_of? Jena::Model::Resource
456
+ next if thing.isAnon
457
+ res_row << RDFS::Resource.new(thing.to_s)
458
+ elsif thing.kind_of? Jena::Model::Literal
459
+ res_row << thing.to_s
460
+ else
461
+ raise ActiveRdfError, "Returned thing other than resource or literal"
462
+ end
463
+ end
464
+ arr_results << res_row
465
+ end
466
+ arr_results
467
+ end
468
+
469
+ def retrieve_lucene_index
470
+ if self.lucene_index_behind?
471
+ builder = LuceneARQ::IndexBuilderSubject.new
472
+ builder.indexStatements(self.model.listStatements)
473
+ builder.closeForWriting
474
+ self.lucene_index = builder.getIndex
475
+ self.lucene_index_behind = false
476
+ end
477
+ self.lucene_index
478
+ end
479
+
480
+ end