jruby-existdb 0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/conf.xml +812 -0
  2. data/lib/existdb.rb +74 -0
  3. data/lib/existdb/classwrap.rb +70 -0
  4. data/lib/existdb/collection.rb +98 -0
  5. data/lib/existdb/dom/active_record.rb +82 -0
  6. data/lib/existdb/dom/mapper.rb +285 -0
  7. data/lib/existdb/embedded.rb +84 -0
  8. data/lib/existdb/index_factory.rb +120 -0
  9. data/lib/existdb/meta.rb +41 -0
  10. data/lib/existdb/resource/base.rb +84 -0
  11. data/lib/existdb/resource/binary.rb +21 -0
  12. data/lib/existdb/resource/xml.rb +23 -0
  13. data/lib/existdb/resource_set.rb +34 -0
  14. data/lib/existdb/xql_factory.rb +191 -0
  15. data/lib/existdb/xquery_service.rb +11 -0
  16. data/lib/jars/antlr-2.7.7.jar +0 -0
  17. data/lib/jars/commons-collections-3.2.1.jar +0 -0
  18. data/lib/jars/commons-logging-1.1.1.jar +0 -0
  19. data/lib/jars/commons-pool-1.5.1.jar +0 -0
  20. data/lib/jars/endorsed/resolver-1.2.jar +0 -0
  21. data/lib/jars/endorsed/serializer-2.9.1.jar +0 -0
  22. data/lib/jars/endorsed/xalan-2.7.1.jar +0 -0
  23. data/lib/jars/endorsed/xercesImpl-2.9.1.jar +0 -0
  24. data/lib/jars/endorsed/xml-apis-1.3.04.jar +0 -0
  25. data/lib/jars/exist-lucene-module.jar +0 -0
  26. data/lib/jars/exist-modules.jar +0 -0
  27. data/lib/jars/exist.jar +0 -0
  28. data/lib/jars/extensions/exist-lucene-module.jar +0 -0
  29. data/lib/jars/extensions/exist-ngram-module.jar +0 -0
  30. data/lib/jars/extensions/exist-versioning.jar +0 -0
  31. data/lib/jars/extensions/lucene-core-2.4.1.jar +0 -0
  32. data/lib/jars/extensions/lucene-regex-2.4.1.jar +0 -0
  33. data/lib/jars/jgroups-all-2.2.6.jar +0 -0
  34. data/lib/jars/jta-1.1.jar +0 -0
  35. data/lib/jars/log4j-1.2.15.jar +0 -0
  36. data/lib/jars/lucene-core-2.4.1.jar +0 -0
  37. data/lib/jars/lucene-regex-2.4.1.jar +0 -0
  38. data/lib/jars/quartz-1.6.5.jar +0 -0
  39. data/lib/jars/sunxacml-1.2.jar +0 -0
  40. data/lib/jars/xmldb.jar +0 -0
  41. data/lib/jars/xmlrpc-client-3.1.2.jar +0 -0
  42. data/lib/jars/xmlrpc-common-3.1.2.jar +0 -0
  43. data/lib/jars/xmlrpc-server-3.1.2.jar +0 -0
  44. data/log4j.xml +190 -0
  45. metadata +104 -0
@@ -0,0 +1,84 @@
1
+ module ExistDB
2
+ class Embedded
3
+ include Singleton
4
+
5
+ attr_accessor :username, :password, :url, :properties
6
+
7
+ def initialize
8
+ @username ||= 'admin'
9
+ @password ||= ''
10
+ @properties ||= {'create-database' => 'true'}
11
+ @url ||= "exist:///db"
12
+
13
+ at_exit do
14
+ stop if running?
15
+ end
16
+ end
17
+
18
+ def start
19
+ return false if running?
20
+ @impl = org.exist.xmldb.DatabaseImpl.new
21
+ @properties.each{ |key, value| @impl.setProperty(key.to_s, value.to_s) }
22
+ org.xmldb.api.DatabaseManager.registerDatabase(@impl)
23
+ @base_collection = @impl.getCollection(@url, @username, @password)
24
+ @database_instance_manager = @base_collection.getService('DatabaseInstanceManager', '1.0')
25
+ @collection_manager = @base_collection.getService('CollectionManager', '1.0')
26
+
27
+ @running = true
28
+ end
29
+
30
+ def running?
31
+ @running
32
+ end
33
+
34
+ alias :started? :running?
35
+
36
+ def stop
37
+ return false if stopped?
38
+ @database_instance_manager.shutdown
39
+ @running = false
40
+ true
41
+ end
42
+
43
+ def stopped?
44
+ not running?
45
+ end
46
+
47
+ def db
48
+ raise InstanceNotRunning if stopped?
49
+ ClassWrap[ @base_collection ]
50
+ end
51
+
52
+ def inspect
53
+ "#<#{self.class}:#{self.hash.to_s(16)} #{running? ? 'running' : 'stopped'}>"
54
+ end
55
+
56
+ def xquery_service
57
+ raise InstanceNotRunning if stopped?
58
+ ClassWrap[ @base_collection.getService('XQueryService', '1.0') ]
59
+ end
60
+
61
+ # org.exist.storage.BrokerPool
62
+ def broker_pool
63
+ raise InstanceNotRunning if stopped?
64
+ org.exist.storage.BrokerPool.getInstance(@impl.getName)
65
+ end
66
+
67
+ # org.exist.storage.txn.Txn
68
+ def transaction # :yields: transaction
69
+ raise InstanceNotRunning if stopped?
70
+ mgr = broker_pool.getTransactionManager
71
+ txn = mgr.beginTransaction
72
+ begin
73
+ yield txn
74
+ mgr.commit(txn)
75
+ rescue
76
+ mgr.abort(txn)
77
+ raise
78
+ end
79
+ end
80
+
81
+ class InstanceNotRunning < Exception; end
82
+ end
83
+
84
+ end
@@ -0,0 +1,120 @@
1
+ module ExistDB
2
+ module IndexFactory
3
+ class << self
4
+ def configure(*opts, &block)
5
+ cfg = IndexFactory.new(*opts, &block)
6
+ cfg.configure
7
+ end
8
+ end
9
+
10
+ # Example:
11
+ # ExistDB::IndexFactory.configure do
12
+ # collection '/db/PartList'
13
+ # range 'qtyOnHand' => Fixnum, 'qtyOnOrder' => Fixnum
14
+ # lucene '//part', 'name', 'description'
15
+ # end
16
+
17
+ class IndexFactory
18
+
19
+ include Meta
20
+
21
+ attr_writer :collection
22
+
23
+ def initialize(*options, &block)
24
+ initialize_with_options(options, [:collection])
25
+ self.instance_eval(&block)
26
+ end
27
+
28
+ def to_s
29
+ wrapper do
30
+ ranges.to_s + lucenes.to_s
31
+ end
32
+ end
33
+
34
+ def range(*targets)
35
+ @ranges ||= Array.new
36
+ targets.each do |target|
37
+ if target.is_a?(Hash) then
38
+ target.each do |key, value|
39
+ tgt_name = key.to_s
40
+ tgt_type = tgt_name.index('/') ? 'path' : 'qname'
41
+ index_type = type_convert(value)
42
+ @ranges << %|<create #{tgt_type}="#{tgt_name}" type="#{index_type}"/>|
43
+ end
44
+ elsif target.respond_to?(:to_s) then
45
+ tgt_name = target.to_s
46
+ tgt_type = tgt_name.index('/') ? 'path' : 'qname'
47
+ @ranges << %|<create #{tgt_type}="#{tgt_name}"/>|
48
+ end
49
+ end
50
+ end
51
+
52
+ def lucene(*targets)
53
+ @lucenes ||= Array.new
54
+ targets.each do |target|
55
+ if target.is_a?(Hash) then
56
+ target.each do |key, value|
57
+ tgt_name = key.to_s
58
+ tgt_type = tgt_name.index('/') ? 'path' : 'qname'
59
+ index_type = type_convert(value)
60
+ @lucenes << %|<text #{tgt_type}="#{tgt_name}" type="#{index_type}"/>|
61
+ end
62
+ elsif target.respond_to?(:to_s) then
63
+ tgt_name = target.to_s
64
+ tgt_type = tgt_name.index('/') ? 'path' : 'qname'
65
+ @lucenes << %|<text #{tgt_type}="#{tgt_name}"/>|
66
+ end
67
+ end
68
+ end
69
+
70
+ def collection(col = nil)
71
+ @collection = col if col
72
+ return @collection
73
+ end
74
+
75
+ def configure
76
+ raise "Specify a collection" unless @collection
77
+ col = collection.create_collection(configuration_collection_name)
78
+ res = Resource.new(:parent => col, :name => configuration_file, :xml => to_s)
79
+ end
80
+
81
+ private
82
+
83
+ def ranges
84
+ @ranges && @ranges.join('')
85
+ end
86
+
87
+ def lucenes
88
+ if @lucenes then
89
+ %|<lucene><analyzer class="org.apache.lucene.analysis.standard.StandardAnalyzer"/><analyzer id="ws" class="org.apache.lucene.analysis.WhitespaceAnalyzer"/>#{ @lucenes.join('') }</lucene>|
90
+ end
91
+ end
92
+
93
+ def wrapper
94
+ %|<collection xmlns="http://exist-db.org/collection-config/1.0"><index>| +
95
+ yield +
96
+ %|</index></collection>|
97
+ end
98
+
99
+ def type_convert(data_type)
100
+ data_type = data_type.to_s
101
+ {
102
+ 'String' => 'xs:string',
103
+ 'Fixnum' => 'xs:int',
104
+ 'Bignum' => 'xs:int',
105
+ 'Numeric' => 'xs:int',
106
+ 'Float' => 'xs:float'
107
+ }[data_type]
108
+ end
109
+
110
+ def configuration_file
111
+ 'collection.xconf'
112
+ end
113
+
114
+ def configuration_collection_name
115
+ "/db/system/config#{@collection}" if @collection
116
+ end
117
+
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,41 @@
1
+ module ExistDB
2
+ module Meta
3
+ # Metaprogramming conviences
4
+
5
+ def initialize_with_options(options, ordered_options)
6
+ # Usage:
7
+ # class MyClass
8
+ # include Meta
9
+ # attr_accessor :opt1, :opt2
10
+ # def initialize(*options)
11
+ # initialize_with_options(options, [:opt1, :opt2])
12
+ # end
13
+ # end
14
+ #
15
+ # obj = MyClass.new(:opt1 => 'foo', :opt2 => 'bar')
16
+ # # OR
17
+ # obj = MyClass.new('foo', 'bar')
18
+ # # MyClass.new now accepts named or ordered params!
19
+ named_or_ordered_options(options, ordered_options).each do |key, value|
20
+ if key.is_a?(Symbol) or key.is_a?(String) then
21
+ key = "#{key}=".to_sym
22
+ self.send(key, value) if self.respond_to?(key)
23
+ end
24
+ end
25
+
26
+ end
27
+
28
+ def named_or_ordered_options(options, ordered_options)
29
+ hash = Hash.new
30
+ options.each_with_index do |option, i|
31
+ if option.is_a?(Hash) then
32
+ hash.merge!(option)
33
+ else
34
+ key = ordered_options[i]
35
+ hash[key] = option
36
+ end
37
+ end
38
+ return hash
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,84 @@
1
+ module ExistDB
2
+ module Resource
3
+
4
+ class << self
5
+ def new(*options)
6
+ if options.size == 1 and not options.first.is_a?(Hash) then
7
+ obj = options.first
8
+ ClassWrap[obj]
9
+ else
10
+ if options.any?{|opt| opt.is_a?(Hash) && ( opt[:xml] || opt[:type] == 'XMLResource' ) } then
11
+ Xml.new(*options)
12
+ elsif options.any?{|opt| opt.is_a?(Hash) && ( opt[:binary] || opt[:type] == 'BinaryResource' ) } then
13
+ Binary.new(*options)
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ class Base
20
+ extend ClassWrappingForwardable
21
+ delegate_to_java(
22
+ :content= => :setContent,
23
+ :content => :getContent,
24
+ :length => :getContentLength,
25
+ :created => :getCreationTime,
26
+ :last_modified => :getLastModificationTime,
27
+ :resource_type => :getResourceType,
28
+ :parent => :getParentCollection,
29
+ :name => :getId
30
+ )
31
+ alias :to_s :content
32
+ alias :size :length
33
+
34
+ def initialize(*opts)
35
+ options = Hash.new
36
+ if opts.size == 1 and not opts.first.is_a?(Hash) then
37
+ @obj = opts.first
38
+ else
39
+ opts.each do |opt|
40
+ if opt.is_a?(Hash) then
41
+ options.merge!(opt)
42
+ end
43
+ end
44
+
45
+ collection = ClassUnwrap[ options[:parent] ]
46
+ data = nil
47
+ if options[:binary]
48
+ type = "BinaryResource"
49
+ data = options[:binary]
50
+ elsif options[:xml]
51
+ type = "XMLResource"
52
+ data = options[:xml]
53
+ else
54
+ type = options[:type]
55
+ data = options[:content]
56
+ end
57
+ type ||= "XMLResource"
58
+
59
+ @obj = collection.createResource(options[:name], type)
60
+ if data then
61
+ self.content = data
62
+ self.save
63
+ end
64
+ end
65
+
66
+ end
67
+
68
+ def inspect
69
+ "#<#{self.class}:0x#{self.hash.to_s(16)} name=#{self.name.inspect}>"
70
+ end
71
+
72
+ def save
73
+ collection = @obj.getParentCollection
74
+ collection.storeResource( @obj )
75
+ true
76
+ end
77
+
78
+ def delete
79
+ parent.delete(self)
80
+ end
81
+
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,21 @@
1
+ module ExistDB
2
+ module Resource
3
+ class Binary < Base
4
+
5
+ def content=(data)
6
+ bytes = data.to_s.to_java_bytes
7
+ @obj.setContent(bytes)
8
+ end
9
+
10
+ def content
11
+ to_io.read
12
+ end
13
+
14
+ def to_io
15
+ input_stream = @obj.getStreamContent
16
+ return Java.java_to_ruby(org.jruby.RubyIO.new(JRuby.runtime, input_stream).java_object)
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,23 @@
1
+ module ExistDB
2
+ module Resource
3
+ class Xml < Base
4
+
5
+ def xquery(*opts)
6
+ parent.xquery.query(self, *opts)
7
+ end
8
+
9
+ def compile(*opts)
10
+ parent.xquery.compile(*opts)
11
+ end
12
+
13
+ def execute(*opts)
14
+ parent.xquery.execute(self, *opts)
15
+ end
16
+
17
+ def dom
18
+ @obj.getContentAsDOM
19
+ end
20
+
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,34 @@
1
+ module ExistDB
2
+ class ResourceSet
3
+
4
+ include Enumerable
5
+ extend ClassWrappingForwardable
6
+ delegate_to_java(
7
+ :size => :getSize,
8
+ :[] => :getResource,
9
+ :join => :getMembersAsResource,
10
+ :add => :addResource
11
+ )
12
+
13
+ def initialize(java_obj)
14
+ @obj = java_obj
15
+ end
16
+
17
+ def each
18
+ for i in (0 .. (size - 1))
19
+ yield self[i]
20
+ end
21
+ end
22
+
23
+ class << self
24
+ def [](*resources)
25
+ set = new( org.exist.xmldb.MapResourceSet.new )
26
+ resources.each do |resource|
27
+ set.add(resource)
28
+ end
29
+ return set
30
+ end
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,191 @@
1
+ module ExistDB
2
+ module XQLFactory
3
+
4
+ class << self
5
+
6
+ # Shorthand for ExistDB::XQLFactory::XQLFactory.new( options ).xquery
7
+
8
+ def Build(*opts, &block)
9
+ XQLFactory.new(*opts, &block).xquery
10
+ end
11
+ end
12
+
13
+ # This is used by the XQLFactory for remapping nodes
14
+ #
15
+ # This could possibly be simplified by using XSLT if there was a Ruby DSL for XSLT
16
+ #
17
+ # The primary goal is to keep the barrier to entry low for rubyists who have limited XML experience
18
+ class Path
19
+
20
+ attr_reader :to_s, :to_a
21
+ def initialize(path)
22
+ if path.is_a?(String) then
23
+ @to_s = path
24
+ @to_a = path.split('/')
25
+ elsif path.is_a?(Array) then
26
+ @to_s = path.join('/')
27
+ @to_a = path
28
+ end
29
+ end
30
+
31
+ def name
32
+ @to_a.last
33
+ end
34
+
35
+ def context
36
+ @to_a[0..-2]
37
+ end
38
+
39
+ def <=>(path)
40
+ to_a <=> path.to_a
41
+ end
42
+
43
+ def common(path)
44
+ a = Array.new
45
+ self.to_a.each_index do |i|
46
+ if self.to_a[i].nil? or
47
+ path.to_a[i].nil? or
48
+ self.to_a[i] != path.to_a[i] then
49
+ break
50
+ else
51
+ a << self.to_a[i]
52
+ end
53
+ end
54
+ a << ''
55
+ return Path.new(a)
56
+ end
57
+
58
+ def switch_context(path)
59
+ output = String.new
60
+ common_path = self.common(path)
61
+ diff = self.context.size - common_path.context.size
62
+ if diff > 0 then
63
+ output << self.context.last(diff).reverse.map{ |node| "</#{node}>"}.join
64
+ end
65
+ diff = path.context.size - common_path.context.size
66
+ if diff > 0 then
67
+ output << path.context.last(diff).map{ |node| "<#{node}>"}.join
68
+ end
69
+ return output
70
+ end
71
+
72
+ end
73
+
74
+ # This is an attempt to create a Ruby DSL for the Most Common XQuery use cases.
75
+ class XQLFactory
76
+
77
+ include Meta
78
+ attr_accessor :start, :max, :doc,
79
+ :node_xpath, :sort, :return_attributes,
80
+ :return_tag, :node_remap, :search, :ftquery
81
+
82
+ # Accepts Ordered or Named Parameters for any of the attr_accessors
83
+ #
84
+ # Ordered Options -- :doc, :start, :max, :sort
85
+ #
86
+ # E.g.
87
+ #
88
+ # <tt>
89
+ # xql = XQLFactory.new("doc('http://example.com')", 5, 10, :node_xpath => '//a').xquery
90
+ # </tt>
91
+ #
92
+ # Would create an XQuery statement to find the fifth through tenth links on example.com
93
+ #
94
+ # See also ExistDB::XQLFactory.Build as a shorthand way of calling this.
95
+
96
+ def initialize(*options)
97
+ initialize_with_options(options, [:doc, :start, :max, :sort])
98
+ yield self if block_given?
99
+ end
100
+
101
+ def limit_statement
102
+ if start or max then
103
+ "let $scope := subsequence($scope, #{start || 1}, #{max || 'count($scope)'})\n"
104
+ else
105
+ ''
106
+ end
107
+ end
108
+
109
+ def sort_statement
110
+ if sort then
111
+ if sort.is_a?(String) then
112
+ "order by $node/#{sort}"
113
+ elsif sort.is_a?(Hash) then
114
+ "order by $node/#{sort.keys.first} #{sort_direction(sort.values.first)}"
115
+ elsif sort.is_a?(Array) then
116
+ "order by " + sort.map{ |h| "$node/#{h.keys.first} #{sort_direction(h.values.first)}" }.join(', ')
117
+ end
118
+ else
119
+ ''
120
+ end
121
+ end
122
+
123
+ def sort_direction(dir)
124
+ dir = dir.to_s.downcase
125
+ if %w|asc ascending|.include?(dir)
126
+ return :ascending
127
+ elsif %|desc descending|.include?(dir)
128
+ return :descending
129
+ else
130
+ return :ascending
131
+ end
132
+ end
133
+
134
+ def search_statement
135
+ "[contains(., #{ search.inspect })]" if search
136
+ "[ft:query(., #{ ftquery.inspect })]" if ftquery
137
+ end
138
+
139
+ def init_statement
140
+ raise "doc attribute required" if doc.nil? or doc.to_s.empty?
141
+ raise "node_xpath attribute required" if node_xpath.nil? or node_xpath.empty?
142
+ "let $scope := for $node in #{doc if doc.is_a?(String)}#{node_xpath}#{search_statement} #{sort_statement} return $node\n"
143
+ end
144
+
145
+ def return_tag
146
+ @return_tag || 'xml'
147
+ end
148
+
149
+ def return_tag=(tag)
150
+ @return_tag = tag
151
+ end
152
+
153
+ def return_statement
154
+ if return_attributes_statement.empty? and @return_tag.nil? then
155
+ "return $scope"
156
+ else
157
+ "return <#{return_tag}#{return_attributes_statement}> { $scope } </#{return_tag}>"
158
+ end
159
+ end
160
+
161
+ def return_attributes_statement
162
+ return '' if return_attributes.nil? or return_attributes.empty?
163
+ ' ' + return_attributes.keys.map{ |key|
164
+ "#{key}=#{return_attributes[key].to_s.inspect}"
165
+ }.join(' ')
166
+ end
167
+
168
+ def node_remap_statement
169
+ return '' if node_remap.nil? or node_remap.empty?
170
+ output = "let $scope := for $node in $scope return\n"
171
+ current_context = Path.new('')
172
+ node_remap.to_a.map{|a|
173
+ a[1] = Path.new(a[1]); a
174
+ }.sort.each do |a|
175
+ (src_path, dest_path) = a
176
+ output << current_context.switch_context(dest_path)
177
+ current_context = dest_path
178
+ output << "<#{dest_path.name}>{ $node#{src_path} }</#{dest_path.name}>"
179
+ end
180
+ output << current_context.switch_context( Path.new('') )
181
+ output << "\n"
182
+ return output
183
+ end
184
+
185
+ def xquery
186
+ init_statement + limit_statement + node_remap_statement + return_statement
187
+ end
188
+
189
+ end
190
+ end
191
+ end