torquebox-cache 2.0.0.beta1-java

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.
@@ -0,0 +1,46 @@
1
+ # Copyright 2008-2011 Red Hat, Inc, and individual contributors.
2
+ #
3
+ # This is free software; you can redistribute it and/or modify it
4
+ # under the terms of the GNU Lesser General Public License as
5
+ # published by the Free Software Foundation; either version 2.1 of
6
+ # the License, or (at your option) any later version.
7
+ #
8
+ # This software is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11
+ # Lesser General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU Lesser General Public
14
+ # License along with this software; if not, write to the Free
15
+ # Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
16
+ # 02110-1301 USA, or see the FSF site: http://www.fsf.org.
17
+
18
+ module TorqueBox
19
+ module Infinispan
20
+ class CacheListener
21
+
22
+ def event_fired( event )
23
+ event_type = event.get_type.to_s.downcase
24
+ if respond_to? event_type
25
+ self.send( event_type, event )
26
+ else
27
+ puts "#{self.class.name}##{event_type} not implemented."
28
+ end
29
+ end
30
+
31
+ add_class_annotation( { org.infinispan.notifications.Listener => { } } )
32
+ add_method_signature( "event_fired", [java.lang.Void::TYPE, org.infinispan.notifications.cachelistener.event.Event] )
33
+ add_method_annotation( "event_fired",
34
+ { org.infinispan.notifications.cachelistener.annotation.CacheEntryCreated => {},
35
+ org.infinispan.notifications.cachelistener.annotation.CacheEntryRemoved => {},
36
+ org.infinispan.notifications.cachelistener.annotation.CacheEntryModified => {},
37
+ org.infinispan.notifications.cachelistener.annotation.CacheEntryEvicted => {},
38
+ org.infinispan.notifications.cachelistener.annotation.CacheEntryActivated => {},
39
+ org.infinispan.notifications.cachelistener.annotation.CacheEntryEvicted => {},
40
+ org.infinispan.notifications.cachelistener.annotation.CacheEntryVisited => {}})
41
+ become_java!
42
+ end
43
+ end
44
+ end
45
+
46
+
@@ -0,0 +1,128 @@
1
+ #
2
+ # Copyright 2011 Red Hat, Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+ require "digest/sha1"
17
+ require 'dm-core'
18
+ require 'cache'
19
+ require 'json'
20
+ require 'torquebox-cache' # is this needed?
21
+ require 'datamapper/model'
22
+ require 'datamapper/search'
23
+
24
+
25
+ module DataMapper::Adapters
26
+
27
+ class InfinispanAdapter < AbstractAdapter
28
+
29
+ include TorqueBox::Infinispan
30
+
31
+ DataMapper::Model.append_inclusions( Infinispan::Model )
32
+
33
+ def initialize( name, options )
34
+ super
35
+ @options = options.dup
36
+ @metadata = @options.dup
37
+ @options[:name] = name.to_s
38
+ @options[:index] = true
39
+ @metadata[:name] = name.to_s + "/metadata"
40
+ @cache = Cache.new( @options )
41
+ @metadata_cache = Cache.new( @metadata )
42
+ @search = Infinispan::Search.new(cache, lambda{ |v| self.deserialize(v) })
43
+ end
44
+
45
+
46
+ def create( resources )
47
+ cache.transaction do
48
+ resources.each do |resource|
49
+ initialize_serial( resource, @metadata_cache.increment( index_for( resource ) ) )
50
+ cache.put( key( resource ), serialize( resource ) )
51
+ end
52
+ end
53
+ end
54
+
55
+ def read( query )
56
+ records = []
57
+ query.filter_records(@search.search( query ))
58
+ end
59
+
60
+ def update( attributes, collection )
61
+ attributes = attributes_as_fields(attributes)
62
+ cache.transaction do
63
+ collection.each do |resource|
64
+ resource.attributes(:field).merge(attributes)
65
+ cache.put( key(resource), serialize(resource) )
66
+ end
67
+ end
68
+ end
69
+
70
+ def delete( collection )
71
+ cache.transaction do
72
+ collection.each do |resource|
73
+ cache.remove( key(resource) )
74
+ end
75
+ end
76
+ end
77
+
78
+ def stop
79
+ cache.stop
80
+ end
81
+
82
+ def serialize(resource)
83
+ resource.is_a?(DataMapper::Resource) ? resource : resource.to_json
84
+ end
85
+
86
+ def deserialize(value)
87
+ value.is_a?(String) ? JSON.parse(value) : value
88
+ end
89
+
90
+ def search_manager
91
+ @search.search_manager
92
+ end
93
+
94
+ private
95
+ def cache
96
+ @cache
97
+ end
98
+
99
+ def metadata_cache
100
+ @metadata_cache
101
+ end
102
+
103
+ def next_id(resource)
104
+ Digest::SHA1.hexdigest(Time.now.to_i + rand(1000000000).to_s)[1..length].to_i
105
+ end
106
+
107
+ def key( resource )
108
+ model = resource.model
109
+ key = resource.key.nil? ? '' : resource.key.join('/')
110
+ "#{model}/#{key}/#{resource.id}"
111
+ end
112
+
113
+ def index_for( resource )
114
+ resource.model.name + ".index"
115
+ end
116
+
117
+ def all_records
118
+ records = []
119
+ cache.keys.each do |key|
120
+ value = cache.get(key)
121
+ records << deserialize(value) if value
122
+ end
123
+ records
124
+ end
125
+
126
+ end
127
+ end
128
+
@@ -0,0 +1,185 @@
1
+ #
2
+ # Copyright 2011 Red Hat, Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+ require 'dm-serializer'
17
+ require 'jruby/core_ext'
18
+ require 'json'
19
+
20
+
21
+ module Infinispan
22
+ JVoid = java.lang.Void::TYPE
23
+
24
+ module Model
25
+
26
+ # TODO enhance TYPEs list
27
+ TYPES = {
28
+ ::String => java.lang.String,
29
+ ::Integer => java.lang.Integer,
30
+ ::Float => java.lang.Double,
31
+ ::BigDecimal => java.math.BigDecimal,
32
+ ::Date => java.util.Date,
33
+ ::DateTime => java.util.Date,
34
+ ::Time => java.util.Date,
35
+ ::TrueClass => java.lang.Boolean
36
+ }
37
+
38
+ def self.included(model)
39
+ model.extend(ClassMethods)
40
+ include java.io.Serializable
41
+
42
+ unless model.mapped? model.name
43
+ [:auto_migrate!, :auto_upgrade!, :create, :all, :copy, :first, :first_or_create, :first_or_new, :get, :last, :load].each do |method|
44
+ model.before_class_method(method, :configure_index)
45
+ end
46
+
47
+ [:save, :update, :destroy, :update_attributes].each do |method|
48
+ model.before(method) { model.configure_index }
49
+ end
50
+ end
51
+ end
52
+
53
+ def deserialize_to
54
+ self.class.name
55
+ end
56
+
57
+ def is_a_with_hack?( thing )
58
+ thing == java.lang.Object || is_a_without_hack?( thing )
59
+ end
60
+
61
+ alias_method :is_a_without_hack?, :is_a?
62
+ alias_method :is_a?, :is_a_with_hack?
63
+
64
+ module ClassMethods
65
+
66
+ @@mapped = {}
67
+
68
+ def auto_upgrade!
69
+ configure_index
70
+ end
71
+
72
+ def auto_migrate!
73
+ destroy
74
+ configure_index
75
+ end
76
+
77
+ def to_java_type(type)
78
+ TYPES[type] || self.to_java_type(type.primitive)
79
+ end
80
+
81
+ def mapped?( type )
82
+ @@mapped[type]
83
+ end
84
+
85
+ def configure_index
86
+ unless mapped?( name )
87
+ configure_index!
88
+ end
89
+ end
90
+
91
+ def configure_index!
92
+ TorqueBox::Infinispan::Cache.log( "Configuring dm-infinispan-adapter model #{name}" )
93
+ properties().each do |prop|
94
+ TorqueBox::Infinispan::Cache.log( "Adding property #{prop.inspect}" )
95
+ add_java_property(prop)
96
+ TorqueBox::Infinispan::Cache.log( "Added property #{prop.inspect}" )
97
+ end
98
+
99
+ annotation = {
100
+ org.hibernate.search.annotations.Indexed => {},
101
+ org.hibernate.search.annotations.ProvidedId => {},
102
+ org.infinispan.marshall.SerializeWith => { "value" => org.torquebox.cache.marshalling.JsonExternalizer.java_class }
103
+ }
104
+
105
+ add_class_annotation( annotation )
106
+
107
+ # Wonder twin powers... ACTIVATE!
108
+ java_class = become_java!(false)
109
+
110
+ @@mapped[name] = true
111
+ end
112
+
113
+ def add_java_property(prop)
114
+ name = prop.name
115
+ type = prop.class
116
+
117
+ column_name = prop.field
118
+ annotation = {}
119
+
120
+ annotation[org.hibernate.search.annotations.Field] = {}
121
+
122
+ get_name = "get#{name.to_s.capitalize}"
123
+ set_name = "set#{name.to_s.capitalize}"
124
+
125
+ # TODO Time, Discriminator, EmbededValue
126
+ # to consider: in mu opinion those methods should set from/get to java objects...
127
+ if (type == DataMapper::Property::Date)
128
+ class_eval <<-EOT
129
+ def #{set_name.intern} (d)
130
+ attribute_set(:#{name} , d.nil? ? nil : Date.civil(d.year + 1900, d.month + 1, d.date))
131
+ end
132
+ EOT
133
+ class_eval <<-EOT
134
+ def #{get_name.intern}
135
+ d = attribute_get(:#{name} )
136
+ java.util.Date.new( (Time.mktime(d.year, d.month, d.day).to_i * 1000) ) if d
137
+ end
138
+ EOT
139
+ elsif (type == DataMapper::Property::DateTime)
140
+ class_eval <<-EOT
141
+ def #{set_name.intern} (d)
142
+ attribute_set(:#{name} , d.nil? ? nil : DateTime.civil(d.year + 1900, d.month + 1, d.date, d.hours, d.minutes, d.seconds))
143
+ end
144
+ EOT
145
+ class_eval <<-EOT
146
+ def #{get_name.intern}
147
+ d = attribute_get(:#{name} )
148
+ java.util.Date.new( (Time.mktime(d.year, d.month, d.day, d.hour, d.min, d.sec, 0).to_i * 1000) ) if d
149
+ end
150
+ EOT
151
+ elsif (type.to_s == BigDecimal || type == DataMapper::Property::Decimal)
152
+ class_eval <<-EOT
153
+ def #{set_name.intern} (d)
154
+ attribute_set(:#{name} , d.nil? ? nil :#{type}.new(d.to_s))
155
+ end
156
+ EOT
157
+ class_eval <<-EOT
158
+ def #{get_name.intern}
159
+ d = attribute_get(:#{name} )
160
+ java.math.BigDecimal.new(d.to_i) if d
161
+ end
162
+ EOT
163
+ else
164
+ class_eval <<-EOT
165
+ def #{set_name.intern} (d)
166
+ attribute_set(:#{name} , d)
167
+ end
168
+ EOT
169
+ class_eval <<-EOT
170
+ def #{get_name.intern}
171
+ d = attribute_get(:#{name} )
172
+ d
173
+ end
174
+ EOT
175
+ end
176
+
177
+ mapped_type = to_java_type(type)
178
+ add_method_signature get_name, [mapped_type]
179
+ add_method_annotation get_name, annotation
180
+ add_method_signature set_name, [JVoid, mapped_type]
181
+ end
182
+ end
183
+
184
+ end
185
+ end
@@ -0,0 +1,145 @@
1
+ #
2
+ # Copyright 2011 Red Hat, Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+
17
+ module Infinispan
18
+
19
+ class Search
20
+
21
+ def initialize(cache, deserializer)
22
+ @cache = cache
23
+ @deserializer = deserializer
24
+ begin
25
+ @search_manager = cache.search_manager
26
+ rescue Exception => e
27
+ cache.log( "Infinispan SearchManager not available for cache: #{cache.name}", 'ERROR' )
28
+ cache.log( e.message, 'ERROR' )
29
+ end
30
+ end
31
+
32
+ def search( query )
33
+ if @search_manager
34
+ cache_query = search_manager.get_query( build_query( query ), query.model.java_class )
35
+ cache_query.list.collect { |record| deserialize(record) }
36
+ else
37
+ cache.all.select do |r|
38
+ record = deserialize(r)
39
+ record.class == query.model
40
+ end
41
+ end
42
+ end
43
+
44
+ def search_manager
45
+ @search_manager
46
+ end
47
+
48
+ private
49
+ def build_query( query )
50
+ builder = search_manager.build_query_builder_for_class( query.model.java_class ).get
51
+ query = query.conditions.nil? ? builder.all.create_query : handle_condition( builder, query.conditions.first )
52
+ #puts "LUCENE QUERY: #{query.to_s}"
53
+ query
54
+ end
55
+
56
+ def handle_condition( builder, condition )
57
+ #puts "CONDITION: #{condition.inspect} <<<>>> #{condition}"
58
+ #puts "CONDITION CLASS: #{condition.class}"
59
+ #puts "CONDITION OPERANDS: #{condition.operands.inspect}" if condition.respond_to? :operands
60
+ #puts "CONDITION VALUE: #{condition.value}"
61
+ #puts "CONDITION SUBJECT: #{condition.subject.inspect}"
62
+ if condition.class == DataMapper::Query::Conditions::OrOperation
63
+ terms = condition.operands.each do |op|
64
+ builder.bool.should( handle_condition( builder, op ) )
65
+ end
66
+ builder.all.create_query
67
+ elsif condition.class == DataMapper::Query::Conditions::NotOperation
68
+ handle_not_operation( builder, condition )
69
+ elsif condition.class == DataMapper::Query::Conditions::EqualToComparison
70
+ handle_equal_to( builder, condition )
71
+ elsif condition.class == DataMapper::Query::Conditions::InclusionComparison
72
+ handle_inclusion( builder, condition )
73
+ elsif condition.class == DataMapper::Query::Conditions::RegexpComparison
74
+ handle_regex( builder, condition )
75
+ else
76
+ builder.all.create_query
77
+ end
78
+ end
79
+
80
+ def handle_regex( builder, condition )
81
+ field = condition.subject.name
82
+ # TODO Figure out how hibernate search/lucene deal with regexp
83
+ value = condition.value.nil? ? "?*" : "*" + condition.value.source.gsub('/','') + "*"
84
+ builder.keyword.wildcard.on_field(field).matching(value).create_query
85
+ end
86
+
87
+ def handle_inclusion( builder, condition )
88
+ #puts "RANGE: #{condition.value.class} #{condition.value}"
89
+ if condition.value.is_a? Range
90
+ # TODO: Deal with Time
91
+ if ((condition.subject.class == DataMapper::Property::DateTime) ||
92
+ (condition.subject.class == DataMapper::Property::Date))
93
+ rng = builder.range.on_field(condition.subject.name).from(convert_date(condition.value.begin)).to(convert_date(condition.value.end))
94
+ else
95
+ rng = builder.range.on_field(condition.subject.name).from(condition.value.begin).to(condition.value.end)
96
+ end
97
+ condition.value.exclude_end? ? rng.exclude_limit.create_query : rng.create_query
98
+ else # an Array
99
+ match = condition.value.collect { |v| v }.join(' ')
100
+ if match.empty?
101
+ # we should find nothing
102
+ builder.bool.must( builder.all.create_query ).not.create_query
103
+ else
104
+ builder.keyword.on_field( condition.subject.name ).matching( match ).create_query
105
+ end
106
+ end
107
+ end
108
+
109
+ def convert_date(date)
110
+ java.util.Date.new(Time.mktime(date.year, date.month, date.day, date.hour, date.min, date.sec, 0).to_i*1000) if date
111
+ end
112
+
113
+ def handle_equal_to( builder, condition )
114
+ field = condition.subject.name
115
+ value = condition.value.nil? ? "?*" : condition.value.to_s
116
+ if !value.nil? && (value.include?( '?' ) || value.include?( '*' ))
117
+ builder.keyword.wildcard.on_field(field).matching(value).create_query
118
+ else
119
+ builder.keyword.on_field(field).matching(value).create_query
120
+ end
121
+ end
122
+
123
+ def handle_not_operation( builder, operation )
124
+ condition = operation.operands.first
125
+ if (condition.class == DataMapper::Query::Conditions::EqualToComparison && condition.value.nil?)
126
+ # not nil means everything
127
+ everything = DataMapper::Query::Conditions::EqualToComparison.new( condition.subject, '*' )
128
+ handle_condition( builder, everything )
129
+ else
130
+ builder.bool.must( handle_condition( builder, condition ) ).not.create_query
131
+ end
132
+ end
133
+
134
+ def cache
135
+ @cache
136
+ end
137
+
138
+ def deserialize(value)
139
+ @deserializer.call(value)
140
+ end
141
+
142
+ end
143
+ end
144
+
145
+