infinispan-ruby-client 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile.lock ADDED
@@ -0,0 +1,27 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ diff-lcs (1.1.2)
5
+ git (1.2.5)
6
+ jeweler (1.6.2)
7
+ bundler (~> 1.0)
8
+ git (>= 1.2.5)
9
+ rake
10
+ rake (0.9.1)
11
+ rspec (2.6.0)
12
+ rspec-core (~> 2.6.0)
13
+ rspec-expectations (~> 2.6.0)
14
+ rspec-mocks (~> 2.6.0)
15
+ rspec-core (2.6.3)
16
+ rspec-expectations (2.6.0)
17
+ diff-lcs (~> 1.1.2)
18
+ rspec-mocks (2.6.0)
19
+
20
+ PLATFORMS
21
+ ruby
22
+
23
+ DEPENDENCIES
24
+ bundler
25
+ jeweler
26
+ rake
27
+ rspec (~> 2.1)
data/README.markdown ADDED
@@ -0,0 +1,75 @@
1
+ # Infinispan Ruby Client
2
+
3
+ [Infinispan](http://www.jboss.org/infinispan) is a scalable, high-availability
4
+ data grid. This is a native Ruby client implementation of Infinispan's [Hotrod
5
+ client/server protocol](http://community.jboss.org/docs/DOC-14421), allowing
6
+ Ruby applications to natively store and retrieve strings and Ruby objects in
7
+ the Infinispan data grid.
8
+
9
+
10
+ ## Introduction
11
+
12
+ This is a [Level-1
13
+ client](http://community.jboss.org/wiki/HotRodProtocol#Client_Intelligence_1_byte),
14
+ meaning that it is a basic client and does not participate in the data grid
15
+ clustering. The client currently only supports using the default cache, and
16
+ all operations of the Hot Rod protocol are implemented except the [server
17
+ stats](http://community.jboss.org/wiki/HotRodProtocol#stats_request)
18
+ request.
19
+
20
+ ## Installation
21
+
22
+ $ gem install infinispan-ruby-client
23
+
24
+ ## Usage
25
+
26
+ Using the infinispan-ruby-client requires that you connect to an Infinispan
27
+ server using the hotrod protocol.
28
+
29
+ 1) First start Infinispan, specifying hotrod for the protocol
30
+
31
+ $INFINISPAN_DIR/bin/startServer.sh -r hotrod
32
+
33
+ 2) Then write code
34
+
35
+ require 'infinispan-ruby-client'
36
+ cache = Infinispan::RemoteCache.new => #<Infinispan::RemoteCache:0x100365a78 @name="", @host="localhost", @port=11222>
37
+
38
+ # Store and retrieve a string
39
+ cache.put("Name", "Lance") => true
40
+ cache.get("Name") => "Lance"
41
+
42
+ # Store and retrieve a ruby object
43
+ cache.put("Time", Time.now) => true
44
+ time = cache.get("Time") => Tue Jun 07 17:45:17 -0400 2011
45
+ time.class => Time
46
+
47
+
48
+ ## Todo
49
+ * Support cache names
50
+ * Add ping operation before any call
51
+ * Validate response headers
52
+ * Get server statistics
53
+ * Support for lifespanSeconds and maxIdleTimeSeconds
54
+ * Support for intelligent clients and topologies, and listeners
55
+ * Configuration files support
56
+ * Transaction support
57
+ * Support for Apache Avro Marshaller ??
58
+
59
+
60
+ ## License
61
+ Copyright 2011 Red Hat, Inc.
62
+
63
+ Licensed under the Apache License, Version 2.0 (the "License");
64
+ you may not use this file except in compliance with the License.
65
+ You may obtain a copy of the License at
66
+
67
+ http://www.apache.org/licenses/LICENSE-2.0
68
+
69
+ Unless required by applicable law or agreed to in writing, software
70
+ distributed under the License is distributed on an "AS IS" BASIS,
71
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
72
+ See the License for the specific language governing permissions and
73
+ limitations under the License.
74
+
75
+
@@ -0,0 +1,67 @@
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
+ module Constants
19
+ MAGIC = 0xA0, 0xA1
20
+ HT_VERSION = 10
21
+
22
+ PUT = 0x01, 0x02
23
+ GET = 0x03, 0x04
24
+ PUT_IF_ABSENT = 0x05, 0x06
25
+ REPLACE = 0x07, 0x08
26
+ REPLACE_IF = 0x09, 0x0A
27
+ REMOVE = 0x0B, 0x0C
28
+ REMOVE_IF = 0x0D, 0x0E
29
+ CONTAINS = 0x0F, 0x10
30
+ GET_WITH_VERSION = 0x11, 0x12
31
+ CLEAR = 0x13, 0x14
32
+ STATS = 0x15, 0x16
33
+ PING = 0x17, 0x18
34
+ BULK_GET = 0x19, 0x1A
35
+ ERROR = 0x50
36
+
37
+ end
38
+
39
+ module ResponseCode
40
+
41
+ SUCCESS = 0x00
42
+ NO_ACTION = 0x01
43
+ INVALID_KEY = 0x02
44
+ INVALID_MAGIC = 0x81
45
+ UNKNOWN_COMMAND = 0x82
46
+ UNKNOWN_VERSION = 0x83
47
+ REQUEST_PARSE_ERROR = 0x84
48
+ SERVER_ERROR = 0x85
49
+ SERVER_TIME_OUT = 0x86
50
+
51
+ def self.status_message( response_code )
52
+ case response_code
53
+ when SUCCESS : "Success"
54
+ when NO_ACTION : "No action taken"
55
+ when INVALID_KEY : "Invalid key supplied"
56
+ when INVALID_MAGIC : "Invalid magic or message"
57
+ when UNKNOWN_COMMAND : "Unrecognized command"
58
+ when UNKNOWN_VERSION : "Unrecognized version"
59
+ when REQUEST_PARSE_ERROR : "Request parse error"
60
+ when SERVER_ERROR : "Server error"
61
+ when SERVER_TIME_OUT : "Server timeout"
62
+ else
63
+ "Unrecognized response code: #{response_code.chr}"
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,39 @@
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
+ require 'infinispan-ruby-client/unsigned.rb'
18
+
19
+ class HeaderBuilder
20
+ include Infinispan::Constants
21
+ def self.getHeader( op_code,cache_name )
22
+ result=[MAGIC[0].chr, #Magic
23
+ Unsigned.encodeVint(0x04), #Message Id
24
+ HT_VERSION.chr, # Version
25
+ op_code.chr, # Opcode
26
+ cache_name.size==0?[]:Unsigned.encodeVint(cache_name.size)] # Cache Name Length
27
+
28
+ result<<cache_name unless cache_name.size==0 # # Cache Name
29
+
30
+ tale=[0.chr, # Flags
31
+ 0x01.chr, # Client Intelligence
32
+ 0.chr, # Topology Id
33
+ 0.chr, # Transaction Type
34
+ 0.chr] # Transaction Id
35
+
36
+ tale.each{|e| result<<e}
37
+ result
38
+ end
39
+ end
@@ -0,0 +1,239 @@
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
+ require 'socket'
18
+
19
+ module Infinispan
20
+ class RemoteCache
21
+ include Infinispan::Constants
22
+
23
+ attr_accessor :host, :port, :name
24
+
25
+ def initialize( host="localhost", port=11222, name="" )
26
+ @host = host
27
+ @port = port
28
+ @name = name
29
+ end
30
+
31
+ def ping
32
+ do_op( :operation => PING[0] )
33
+ end
34
+
35
+ def clear
36
+ do_op( :operation => CLEAR[0] )
37
+ end
38
+
39
+ def get( key )
40
+ do_op( :operation => GET[0], :key => key )
41
+ end
42
+
43
+ def get_bulk( count = 0 )
44
+ do_op( :operation => BULK_GET[0], :count => count )
45
+ end
46
+
47
+ def put( key, value )
48
+ do_op( :operation => PUT[0], :key => key, :value => value )
49
+ end
50
+
51
+ def put_if_absent( key, value )
52
+ do_op( :operation => PUT_IF_ABSENT[0], :key => key, :value => value )
53
+ end
54
+
55
+ def get_versioned( key )
56
+ do_op( :operation => GET_WITH_VERSION[0], :key => key )
57
+ end
58
+
59
+ def contains_key?( key )
60
+ do_op( :operation => CONTAINS[0], :key => key )
61
+ end
62
+
63
+ alias_method :contains_key, :contains_key?
64
+
65
+ def remove( key )
66
+ do_op( :operation => REMOVE[0], :key => key )
67
+ end
68
+
69
+ def remove_if_unmodified( key, version )
70
+ do_op( :operation => REMOVE_IF[0], :key => key, :version => version )
71
+ end
72
+
73
+ def replace( key, value )
74
+ do_op( :operation => REPLACE[0], :key => key, :value => value )
75
+ end
76
+
77
+ def replace_if_unmodified( key, version, value )
78
+ do_op( :operation => REPLACE_IF[0], :key => key, :value => value, :version => version )
79
+ end
80
+
81
+ private
82
+ def do_op( options )
83
+ options[:cache] ||= @name
84
+
85
+ send_op = Operation.send[ options[:operation] ]
86
+ recv_op = Operation.receive[ options[:operation] ]
87
+
88
+ if (send_op && recv_op)
89
+ TCPSocket.open( @host, @port ) do |connection|
90
+ send_op.call( connection, options )
91
+ recv_op.call( connection )
92
+ end
93
+ else
94
+ raise "Unexpected operation: #{options[:operation]}"
95
+ end
96
+
97
+ end
98
+ end
99
+
100
+ module Operation
101
+
102
+ include Infinispan::Constants
103
+ include Infinispan::ResponseCode
104
+
105
+ def self.send
106
+ {
107
+ GET[0] => KEY_ONLY_SEND,
108
+ GET_WITH_VERSION[0] => KEY_ONLY_SEND,
109
+ BULK_GET[0] => BULK_GET_SEND,
110
+ PUT[0] => KEY_VALUE_SEND,
111
+ REMOVE[0] => KEY_ONLY_SEND,
112
+ REMOVE_IF[0] => REMOVE_IF_SEND,
113
+ CONTAINS[0] => KEY_ONLY_SEND,
114
+ PUT_IF_ABSENT[0] => KEY_VALUE_SEND,
115
+ REPLACE[0] => KEY_VALUE_SEND,
116
+ REPLACE_IF[0] => REPLACE_IF_SEND,
117
+ CLEAR[0] => HEADER_ONLY_SEND,
118
+ PING[0] => HEADER_ONLY_SEND
119
+ }
120
+ end
121
+
122
+ def self.receive
123
+ {
124
+ GET[0] => KEY_ONLY_RECV,
125
+ GET_WITH_VERSION[0] => GET_WITH_VERSION_RECV,
126
+ BULK_GET[0] => BULK_GET_RECV,
127
+ PUT[0] => BASIC_RECV,
128
+ REMOVE[0] => BASIC_RECV,
129
+ REMOVE_IF[0] => BASIC_RECV,
130
+ CONTAINS[0] => BASIC_RECV,
131
+ PUT_IF_ABSENT[0] => BASIC_RECV,
132
+ REPLACE[0] => BASIC_RECV,
133
+ REPLACE_IF[0] => BASIC_RECV,
134
+ CLEAR[0] => BASIC_RECV,
135
+ PING[0] => BASIC_RECV
136
+ }
137
+ end
138
+
139
+ HEADER_ONLY_SEND = lambda { |connection, options|
140
+ connection.write( HeaderBuilder.getHeader(options[:operation], options[:cache]) )
141
+ }
142
+
143
+ BULK_GET_SEND = lambda { |connection, options|
144
+ connection.write( HeaderBuilder.getHeader(options[:operation], options[:cache]) )
145
+ connection.write( Unsigned.encodeVint( options[:count] ) )
146
+ }
147
+
148
+ KEY_ONLY_SEND = lambda { |connection, options|
149
+ connection.write( HeaderBuilder.getHeader(options[:operation], options[:cache]) )
150
+ mkey = Marshal.dump( options[:key] )
151
+ connection.write( Unsigned.encodeVint( mkey.size ) )
152
+ connection.write( mkey )
153
+ }
154
+
155
+ KEY_VALUE_SEND = lambda { |connection, options|
156
+ connection.write( HeaderBuilder.getHeader(options[:operation], options[:cache]) )
157
+ # write key
158
+ mkey = Marshal.dump( options[:key] )
159
+ connection.write( Unsigned.encodeVint( mkey.size ) )
160
+ connection.write( mkey )
161
+
162
+ # lifespan + max_idle (not supported yet)
163
+ connection.write( [0x00.chr,0x00.chr] )
164
+
165
+ # write value
166
+ mkey = Marshal.dump( options[:value] )
167
+ connection.write( Unsigned.encodeVint( mkey.size ) )
168
+ connection.write( mkey )
169
+ }
170
+
171
+ REMOVE_IF_SEND = lambda { |connection, options|
172
+ connection.write( HeaderBuilder.getHeader(options[:operation], options[:cache]) )
173
+
174
+ # write key
175
+ key = Marshal.dump( options[:key] )
176
+ connection.write( Unsigned.encodeVint( key.size ) )
177
+ connection.write( key )
178
+
179
+ # write version
180
+ connection.write( options[:version] )
181
+ }
182
+
183
+ REPLACE_IF_SEND = lambda { |connection, options|
184
+ connection.write( HeaderBuilder.getHeader(options[:operation], options[:cache]) )
185
+
186
+ # write key
187
+ key = Marshal.dump( options[:key] )
188
+ connection.write( Unsigned.encodeVint( key.size ) )
189
+ connection.write( key )
190
+
191
+ # lifespan + max_idle (not supported yet)
192
+ connection.write( [0x00.chr,0x00.chr] )
193
+
194
+ # write version
195
+ connection.write( options[:version] )
196
+
197
+ # write value
198
+ value = Marshal.dump( options[:value] )
199
+ connection.write( Unsigned.encodeVint( value.size ) )
200
+ connection.write( value )
201
+ }
202
+
203
+ KEY_ONLY_RECV = lambda { |connection|
204
+ connection.read( 5 ) # The response header
205
+ response_body_length = Unsigned.decodeVint( connection )
206
+ response_body = connection.read( response_body_length )
207
+ Marshal.load( response_body )
208
+ }
209
+
210
+ GET_WITH_VERSION_RECV = lambda { |connection|
211
+ response_header = connection.read( 5 ) # The response header
212
+ version = connection.read( 8 )
213
+ response_body_length = Unsigned.decodeVint( connection )
214
+ response_body = connection.read( response_body_length )
215
+ [ version, Marshal.load( response_body ) ]
216
+ }
217
+
218
+ BULK_GET_RECV = lambda { |connection|
219
+ response = {}
220
+ response_header = connection.read( 5 ) # The response header
221
+ more = connection.read(1).unpack('c')[0]
222
+ while (more == 1) # The "more" flag
223
+ key_length = Unsigned.decodeVint( connection )
224
+ key = connection.read( key_length )
225
+ value_length = Unsigned.decodeVint( connection )
226
+ value = connection.read( value_length )
227
+ response[Marshal.load(key)] = Marshal.load(value)
228
+ more = connection.read(1).unpack('c')[0]
229
+ end
230
+ response
231
+ }
232
+
233
+ BASIC_RECV = lambda { |connection|
234
+ header = connection.read( 5 ) # Just the response header
235
+ header[3] == SUCCESS
236
+ }
237
+
238
+ end
239
+ end
@@ -0,0 +1,39 @@
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
+ class Unsigned
18
+ def self.encodeVint(i)
19
+ result=[]
20
+ while ((i & ~0x7f) != 0)
21
+ result[result.size]=((i & 0x7f) | 0x80).chr
22
+ i>>=7
23
+ end
24
+ result[result.size]=i.chr
25
+ result
26
+ end
27
+
28
+ def self.decodeVint(input)
29
+ b = input.read(1)
30
+ i = b[0] & 0x7F
31
+ shift = 7
32
+ while ((b[0] & 0x80) != 0)
33
+ b = input.read(1)
34
+ i |= (b[0] & 0x7F) << shift
35
+ shift += 7
36
+ end
37
+ i
38
+ end
39
+ end
@@ -0,0 +1,20 @@
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
+ require 'infinispan-ruby-client/constants.rb'
18
+ require 'infinispan-ruby-client/headerbuilder.rb'
19
+ require 'infinispan-ruby-client/remotecache.rb'
20
+ require 'infinispan-ruby-client/unsigned.rb'
metadata ADDED
@@ -0,0 +1,129 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: infinispan-ruby-client
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Lance Ball
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-06-08 00:00:00 -04:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ type: :runtime
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 3
29
+ segments:
30
+ - 0
31
+ version: "0"
32
+ name: bundler
33
+ version_requirements: *id001
34
+ prerelease: false
35
+ - !ruby/object:Gem::Dependency
36
+ type: :development
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ hash: 3
43
+ segments:
44
+ - 0
45
+ version: "0"
46
+ name: jeweler
47
+ version_requirements: *id002
48
+ prerelease: false
49
+ - !ruby/object:Gem::Dependency
50
+ type: :development
51
+ requirement: &id003 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ hash: 3
57
+ segments:
58
+ - 0
59
+ version: "0"
60
+ name: rake
61
+ version_requirements: *id003
62
+ prerelease: false
63
+ - !ruby/object:Gem::Dependency
64
+ type: :development
65
+ requirement: &id004 !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ~>
69
+ - !ruby/object:Gem::Version
70
+ hash: 1
71
+ segments:
72
+ - 2
73
+ - 1
74
+ version: "2.1"
75
+ name: rspec
76
+ version_requirements: *id004
77
+ prerelease: false
78
+ description: infinispan-hotrod-client provides native ruby access to the Infinispan Hotrod API
79
+ email: lball@redhat.com
80
+ executables: []
81
+
82
+ extensions: []
83
+
84
+ extra_rdoc_files:
85
+ - README.markdown
86
+ files:
87
+ - Gemfile.lock
88
+ - README.markdown
89
+ - lib/infinispan-ruby-client.rb
90
+ - lib/infinispan-ruby-client/constants.rb
91
+ - lib/infinispan-ruby-client/headerbuilder.rb
92
+ - lib/infinispan-ruby-client/remotecache.rb
93
+ - lib/infinispan-ruby-client/unsigned.rb
94
+ has_rdoc: true
95
+ homepage: https://github.com/lance/infinispan-ruby-client
96
+ licenses:
97
+ - MIT
98
+ post_install_message:
99
+ rdoc_options: []
100
+
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ none: false
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ hash: 3
109
+ segments:
110
+ - 0
111
+ version: "0"
112
+ required_rubygems_version: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ hash: 3
118
+ segments:
119
+ - 0
120
+ version: "0"
121
+ requirements: []
122
+
123
+ rubyforge_project:
124
+ rubygems_version: 1.6.2
125
+ signing_key:
126
+ specification_version: 3
127
+ summary: Infinispan Hotrod Client
128
+ test_files: []
129
+