torquebox-cache 2.0.0.beta1-java

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