jruby-memcached 0.3.0 → 0.4.0
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/CHANGELOG.md +34 -0
- data/Gemfile.lock +1 -1
- data/README.md +7 -2
- data/lib/memcached.rb +8 -1
- data/lib/memcached/version.rb +1 -1
- data/spec/memcached_spec.rb +79 -1
- data/spec/rails_spec.rb +174 -0
- data/src/main/java/com/openfeint/memcached/Memcached.java +214 -193
- data/src/main/java/com/openfeint/memcached/MemcachedService.java +34 -0
- data/src/main/java/com/openfeint/memcached/Rails.java +238 -0
- data/src/main/java/com/openfeint/memcached/error/Error.java +27 -0
- data/src/main/java/com/openfeint/memcached/error/NotFound.java +7 -0
- data/src/main/java/com/openfeint/memcached/error/NotStored.java +7 -0
- data/src/main/java/com/openfeint/memcached/error/NotSupport.java +7 -0
- data/src/main/java/com/openfeint/memcached/{MarshalTranscoder.java → transcoder/MarshalTranscoder.java} +5 -4
- data/src/main/java/com/openfeint/memcached/{MarshalZlibTranscoder.java → transcoder/MarshalZlibTranscoder.java} +10 -9
- data/target/spymemcached-ext-0.0.1.jar +0 -0
- metadata +96 -80
- data/lib/memcached/exceptions.rb +0 -4
- data/src/main/java/com/openfeint/memcached/MemcachedLibrary.java +0 -52
data/CHANGELOG.md
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
## 0.4.0 (Aug 16, 2012)
|
2
|
+
|
3
|
+
Bugfixes:
|
4
|
+
|
5
|
+
- set as daemon thread to avoid suspend ruby process (like rake task)
|
6
|
+
|
7
|
+
Features:
|
8
|
+
|
9
|
+
- support get with multiple keys
|
10
|
+
- add Memcached::Rails as rails cache_store
|
11
|
+
- use jruby annotation to reduce method definitions
|
12
|
+
|
13
|
+
## 0.3.0 (Aug 7, 2012)
|
14
|
+
|
15
|
+
Features:
|
16
|
+
|
17
|
+
- rewrite with pure jruby implementation
|
18
|
+
|
19
|
+
## 0.2.0 (Jul 29, 2012)
|
20
|
+
|
21
|
+
Bugfixes:
|
22
|
+
|
23
|
+
- set method should not be async
|
24
|
+
|
25
|
+
Features:
|
26
|
+
|
27
|
+
- allow to change hash, distribution and binary protocol
|
28
|
+
|
29
|
+
## 0.1.0 (Jul 24, 2012)
|
30
|
+
|
31
|
+
Features:
|
32
|
+
|
33
|
+
- wrap java library spymemcached
|
34
|
+
- compatible hash and distribution algorithms with memcached.gem
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -35,11 +35,16 @@ You can get multiple values at once:
|
|
35
35
|
$cache.get ['test', 'test2', 'missing']
|
36
36
|
#=> {"test" => "hello", "test2" => "hello"}
|
37
37
|
|
38
|
+
## Rails
|
39
|
+
|
40
|
+
# config/environment.rb
|
41
|
+
config.cache_store = Memcached::Rails.new(:servers => ['127.0.0.1'])
|
42
|
+
|
38
43
|
## Benchmarks
|
39
44
|
|
40
45
|
memcached.gem is the fastest memcached gem in MRI,
|
41
46
|
jruby-memcached is the fastest memcached gem in JRuby.
|
42
47
|
See [benchmark][1]
|
43
48
|
|
44
|
-
[0]:
|
45
|
-
[1]:
|
49
|
+
[0]: https://github.com/evan/memcached
|
50
|
+
[1]: https://github.com/aurorafeint/jruby-memcached/blob/master/benchmark.rb
|
data/lib/memcached.rb
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
require 'java'
|
2
2
|
require 'memcached/version'
|
3
3
|
require File.join(File.dirname(__FILE__), '../target/spymemcached-ext-0.0.1.jar')
|
4
|
+
require 'com/openfeint/memcached/memcached'
|
4
5
|
|
5
|
-
|
6
|
+
class Memcached::Rails
|
7
|
+
attr_reader :logger
|
8
|
+
|
9
|
+
def logger=(logger)
|
10
|
+
@logger = logger
|
11
|
+
end
|
12
|
+
end
|
data/lib/memcached/version.rb
CHANGED
data/spec/memcached_spec.rb
CHANGED
@@ -1,16 +1,23 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
com.openfeint.memcached.MemcachedLibrary.new.load(JRuby.runtime, false)
|
4
3
|
describe Memcached do
|
5
4
|
context "localhost" do
|
6
5
|
before(:all) { @memcached = Memcached.new(["127.0.0.1:11211"]) }
|
7
6
|
after(:all) { @memcached.shutdown }
|
8
7
|
|
9
8
|
it "should get all servers" do
|
9
|
+
@memcached.set "foo", "bar"
|
10
10
|
@memcached.servers.should == ["127.0.0.1:11211"]
|
11
11
|
end
|
12
12
|
|
13
13
|
context "initialize" do
|
14
|
+
it "should connect to 127.0.0.1:11211 if no server defined" do
|
15
|
+
memcached = Memcached.new
|
16
|
+
memcached.set "foo", "bar"
|
17
|
+
memcached.servers.should == ["127.0.0.1:11211"]
|
18
|
+
memcached.shutdown
|
19
|
+
end
|
20
|
+
|
14
21
|
it "should raise error with unsupported option hash" do
|
15
22
|
lambda { Memcached.new("127.0.0.1:11211", :hash => :unknown) }.should raise_error(Memcached::NotSupport)
|
16
23
|
end
|
@@ -42,6 +49,24 @@ describe Memcached do
|
|
42
49
|
@memcached.delete "key" rescue nil
|
43
50
|
lambda { @memcached.get "key" }.should raise_error(Memcached::NotFound)
|
44
51
|
end
|
52
|
+
|
53
|
+
context "multiget" do
|
54
|
+
it "should get hash containing multiple key/value pairs" do
|
55
|
+
@memcached.set "key1", "value1"
|
56
|
+
@memcached.set "key2", "value2"
|
57
|
+
@memcached.get(["key1", "key2"]).should == {"key1" => "value1", "key2" => "value2"}
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should get hash containing nil value" do
|
61
|
+
@memcached.set "key", nil, 0
|
62
|
+
@memcached.get(["key"]).should == {"key" => nil}
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should get empty hash" do
|
66
|
+
@memcached.delete "key" rescue nil
|
67
|
+
@memcached.get(["key"]).should be_empty
|
68
|
+
end
|
69
|
+
end
|
45
70
|
end
|
46
71
|
|
47
72
|
context "set" do
|
@@ -99,6 +124,14 @@ describe Memcached do
|
|
99
124
|
@memcached.delete "key" rescue nil
|
100
125
|
lambda { @memcached.delete "key" }.should raise_error(Memcached::NotFound)
|
101
126
|
end
|
127
|
+
|
128
|
+
#context "incr/decr" do
|
129
|
+
#it "should incr key" do
|
130
|
+
#@memcached.incr "intkey"
|
131
|
+
#@memcached.incr "intkey"
|
132
|
+
#@memcached.get("intkey").should == 1
|
133
|
+
#end
|
134
|
+
#end
|
102
135
|
end
|
103
136
|
|
104
137
|
context "flush" do
|
@@ -110,5 +143,50 @@ describe Memcached do
|
|
110
143
|
lambda { @memcached.get "key2" }.should raise_error(Memcached::NotFound)
|
111
144
|
end
|
112
145
|
end
|
146
|
+
|
147
|
+
context "namespace/prefix_key" do
|
148
|
+
it "should get/set with namespace" do
|
149
|
+
memcached = Memcached.new("127.0.0.1:11211", :namespace => "jruby")
|
150
|
+
memcached.set "key", "value"
|
151
|
+
memcached.get("key").should == "value"
|
152
|
+
memcached.shutdown
|
153
|
+
@memcached.get("jrubykey").should == "value"
|
154
|
+
end
|
155
|
+
|
156
|
+
context "prefix_key" do
|
157
|
+
before(:all) { @prefix_memcached = Memcached.new("127.0.0.1:11211", :prefix_key => "jruby") }
|
158
|
+
after(:all) { @prefix_memcached.shutdown }
|
159
|
+
|
160
|
+
it "should get/set with prefix_key" do
|
161
|
+
@prefix_memcached.set "key", "value"
|
162
|
+
@prefix_memcached.get("key").should == "value"
|
163
|
+
@memcached.get("jrubykey").should == "value"
|
164
|
+
end
|
165
|
+
|
166
|
+
#it "should incr/decr with prefix_key" do
|
167
|
+
#@prefix_memcached.incr "intkey"
|
168
|
+
#@prefix_memcached.decr "intkey"
|
169
|
+
#@memcached.get("jrubyintkey").should == 0
|
170
|
+
#end
|
171
|
+
|
172
|
+
it "should add/replace with prefix_key" do
|
173
|
+
@prefix_memcached.add "newkey", "value"
|
174
|
+
@prefix_memcached.replace "newkey", "new_value"
|
175
|
+
@memcached.get("jrubynewkey").should == "new_value"
|
176
|
+
end
|
177
|
+
|
178
|
+
it "should delete with prefix_key" do
|
179
|
+
@prefix_memcached.set "key", "value"
|
180
|
+
@prefix_memcached.delete "key"
|
181
|
+
lambda { @memcached.get("jrubykey") }.should raise_error(Memcached::NotFound)
|
182
|
+
end
|
183
|
+
|
184
|
+
it "should multiget with prefix_key" do
|
185
|
+
@prefix_memcached.set "key1", "value1"
|
186
|
+
@prefix_memcached.set "key2", "value2"
|
187
|
+
@prefix_memcached.get(["key1", "key2"]).should == {"key1" => "value1", "key2" => "value2"}
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
113
191
|
end
|
114
192
|
end
|
data/spec/rails_spec.rb
ADDED
@@ -0,0 +1,174 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Memcached::Rails do
|
4
|
+
context "localhost" do
|
5
|
+
before(:all) { @memcached = Memcached::Rails.new(:servers => ["127.0.0.1:11211"]) }
|
6
|
+
after(:all) { @memcached.shutdown }
|
7
|
+
|
8
|
+
it "should get all servers" do
|
9
|
+
@memcached.set "foo", "bar"
|
10
|
+
@memcached.servers.should == ["127.0.0.1:11211"]
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should get logger" do
|
14
|
+
require 'logger'
|
15
|
+
@memcached.logger = Logger.new(STDOUT)
|
16
|
+
@memcached.logger.should_not be_nil
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should be active" do
|
20
|
+
@memcached.should be_active
|
21
|
+
end
|
22
|
+
|
23
|
+
context "get" do
|
24
|
+
it "should get value" do
|
25
|
+
@memcached.set "key", "value"
|
26
|
+
@memcached.get("key").should == "value"
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should get nil if key is missing" do
|
30
|
+
@memcached.delete "key" rescue nil
|
31
|
+
@memcached.get("key").should be_nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context "read" do
|
36
|
+
it "should get value" do
|
37
|
+
@memcached.set "key", "value"
|
38
|
+
@memcached.get("key").should == "value"
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should get nil if key is missing" do
|
42
|
+
@memcached.delete "key" rescue nil
|
43
|
+
@memcached.get("key").should be_nil
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context "exist?" do
|
48
|
+
it "should return true if key exists" do
|
49
|
+
@memcached.set "key", "value"
|
50
|
+
@memcached.exist?("key").should be_true
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should return false if key is missing" do
|
54
|
+
@memcached.delete "key" rescue nil
|
55
|
+
@memcached.exist?("key").should be_false
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context "get_multi" do
|
60
|
+
it "should get hash containing multiple key/value pairs" do
|
61
|
+
@memcached.set "key1", "value1"
|
62
|
+
@memcached.set "key2", "value2"
|
63
|
+
@memcached.get(["key1", "key2"]).should == {"key1" => "value1", "key2" => "value2"}
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should get hash containing nil value" do
|
67
|
+
@memcached.set "key", nil, 0
|
68
|
+
@memcached.get(["key"]).should == {"key" => nil}
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should get empty hash" do
|
72
|
+
@memcached.delete "key" rescue nil
|
73
|
+
@memcached.get(["key"]).should be_empty
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context "set" do
|
78
|
+
it "should set successfully" do
|
79
|
+
@memcached.set("key", "value").should be_true
|
80
|
+
@memcached.get("key").should == "value"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context "write" do
|
85
|
+
it "should write successfully" do
|
86
|
+
@memcached.write("key", "value").should be_true
|
87
|
+
@memcached.read("key").should == "value"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context "fetch" do
|
92
|
+
it "should read if key exists" do
|
93
|
+
@memcached.write("key", "value")
|
94
|
+
@memcached.fetch("key") { "new value" }.should == "value"
|
95
|
+
end
|
96
|
+
|
97
|
+
it "should write if key is missing" do
|
98
|
+
@memcached.delete "key" rescue nil
|
99
|
+
@memcached.fetch("key") { "new value" }.should == "new value"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
context "add" do
|
104
|
+
it "should add if key is missing" do
|
105
|
+
@memcached.delete "key" rescue nil
|
106
|
+
@memcached.add("key", "value").should be_true
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should do nothing if key exists" do
|
110
|
+
@memcached.set "key", "value"
|
111
|
+
@memcached.add("key", "value").should be_false
|
112
|
+
end
|
113
|
+
|
114
|
+
context "with string_return_types" do
|
115
|
+
before(:all) { @string_memcached = Memcached::Rails.new("127.0.0.1:11211", :string_return_types => true) }
|
116
|
+
after(:all) { @string_memcached.quit }
|
117
|
+
|
118
|
+
it "should add if key is missing" do
|
119
|
+
@string_memcached.delete "key" rescue nil
|
120
|
+
@string_memcached.add("key", "value").should == "STORED\r\n"
|
121
|
+
end
|
122
|
+
|
123
|
+
it "should do nothing if key exists" do
|
124
|
+
@string_memcached.set "key", "value"
|
125
|
+
@string_memcached.add("key", "value").should == "NOT STORED\r\n"
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
context "delete" do
|
131
|
+
it "should delete existing key" do
|
132
|
+
@memcached.set "key", "value"
|
133
|
+
@memcached.delete "key"
|
134
|
+
@memcached.get("key").should be_nil
|
135
|
+
end
|
136
|
+
|
137
|
+
it "should do nothing if delete missing key" do
|
138
|
+
@memcached.delete "key" rescue nil
|
139
|
+
@memcached.delete "key"
|
140
|
+
@memcached.get("key").should be_nil
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
context "alias" do
|
145
|
+
it "should respond_to? flush_all" do
|
146
|
+
@memcached.should be_respond_to(:flush_all)
|
147
|
+
end
|
148
|
+
|
149
|
+
it "should respond_to? clear" do
|
150
|
+
@memcached.should be_respond_to(:clear)
|
151
|
+
end
|
152
|
+
|
153
|
+
it "should respond_to? :[]" do
|
154
|
+
@memcached.should be_respond_to(:"[]")
|
155
|
+
end
|
156
|
+
|
157
|
+
it "should respond_to? :[]=" do
|
158
|
+
@memcached.should be_respond_to(:"[]=")
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
context "read_multi" do
|
163
|
+
it "should read hash containing multiple key/value pairs" do
|
164
|
+
@memcached.write "key1", "value1"
|
165
|
+
@memcached.write "key2", "value2"
|
166
|
+
@memcached.read_multi(["key1", "key2"]).should == {"key1" => "value1", "key2" => "value2"}
|
167
|
+
end
|
168
|
+
|
169
|
+
it "should read empty hash without params" do
|
170
|
+
@memcached.read_multi.should be_empty
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
@@ -1,15 +1,9 @@
|
|
1
1
|
package com.openfeint.memcached;
|
2
2
|
|
3
|
-
import
|
4
|
-
import
|
5
|
-
import
|
6
|
-
import java.util.ArrayList;
|
7
|
-
import java.util.Arrays;
|
8
|
-
import java.util.List;
|
9
|
-
import java.util.Map;
|
10
|
-
import java.util.concurrent.ExecutionException;
|
3
|
+
import com.openfeint.memcached.error.Error;
|
4
|
+
import com.openfeint.memcached.transcoder.MarshalTranscoder;
|
5
|
+
import com.openfeint.memcached.transcoder.MarshalZlibTranscoder;
|
11
6
|
import net.spy.memcached.AddrUtil;
|
12
|
-
import net.spy.memcached.CachedData;
|
13
7
|
import net.spy.memcached.ConnectionFactoryBuilder;
|
14
8
|
import net.spy.memcached.ConnectionFactoryBuilder.Locator;
|
15
9
|
import net.spy.memcached.ConnectionFactoryBuilder.Protocol;
|
@@ -17,127 +11,68 @@ import net.spy.memcached.DefaultHashAlgorithm;
|
|
17
11
|
import net.spy.memcached.MemcachedClient;
|
18
12
|
import net.spy.memcached.transcoders.Transcoder;
|
19
13
|
import org.jruby.Ruby;
|
14
|
+
import org.jruby.RubyArray;
|
15
|
+
import org.jruby.RubyBoolean;
|
20
16
|
import org.jruby.RubyClass;
|
21
|
-
import org.jruby.RubyException;
|
22
17
|
import org.jruby.RubyHash;
|
23
18
|
import org.jruby.RubyObject;
|
19
|
+
import org.jruby.RubyString;
|
24
20
|
import org.jruby.anno.JRubyClass;
|
25
21
|
import org.jruby.anno.JRubyMethod;
|
26
|
-
import org.jruby.exceptions.RaiseException;
|
27
22
|
import org.jruby.runtime.ThreadContext;
|
28
23
|
import org.jruby.runtime.builtin.IRubyObject;
|
29
24
|
|
30
|
-
|
31
|
-
|
25
|
+
import java.io.IOException;
|
26
|
+
import java.net.InetSocketAddress;
|
27
|
+
import java.net.SocketAddress;
|
28
|
+
import java.util.ArrayList;
|
29
|
+
import java.util.List;
|
30
|
+
import java.util.Map;
|
31
|
+
import java.util.concurrent.ExecutionException;
|
32
32
|
|
33
|
+
@JRubyClass(name = "Memcached")
|
34
|
+
public class Memcached extends RubyObject {
|
33
35
|
private MemcachedClient client;
|
34
36
|
|
35
37
|
private Transcoder<IRubyObject> transcoder;
|
36
38
|
|
37
39
|
private int ttl;
|
38
40
|
|
41
|
+
private String prefixKey;
|
42
|
+
|
39
43
|
public Memcached(final Ruby ruby, RubyClass rubyClass) {
|
40
44
|
super(ruby, rubyClass);
|
41
|
-
this.ruby = ruby;
|
42
|
-
}
|
43
45
|
|
44
|
-
|
45
|
-
|
46
|
-
return initialize(context, servers, context.nil);
|
46
|
+
ttl = 604800;
|
47
|
+
prefixKey = "";
|
47
48
|
}
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
49
|
+
|
50
|
+
@JRubyMethod(name = "initialize", optional = 2)
|
51
|
+
public IRubyObject initialize(ThreadContext context, IRubyObject[] args) {
|
52
|
+
Ruby ruby = context.getRuntime();
|
53
|
+
RubyHash options;
|
54
|
+
if (args.length > 1) {
|
55
|
+
options = args[1].convertToHash();
|
53
56
|
} else {
|
54
|
-
|
57
|
+
options = new RubyHash(ruby);
|
55
58
|
}
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
String defaultTTL = null;
|
63
|
-
String transcoderValue = null;
|
64
|
-
if (!options.isNil()) {
|
65
|
-
RubyHash opts = options.convertToHash();
|
66
|
-
if (opts.containsKey(ruby.newSymbol("distribution"))) {
|
67
|
-
distributionValue = opts.get(ruby.newSymbol("distribution")).toString();
|
68
|
-
}
|
69
|
-
if (opts.containsKey(ruby.newSymbol("hash"))) {
|
70
|
-
hashValue = opts.get(ruby.newSymbol("hash")).toString();
|
71
|
-
}
|
72
|
-
if (opts.containsKey(ruby.newSymbol("binary_protocol"))) {
|
73
|
-
binaryValue = opts.get(ruby.newSymbol("binary_protocol")).toString();
|
74
|
-
}
|
75
|
-
if (opts.containsKey(ruby.newSymbol("default_ttl"))) {
|
76
|
-
defaultTTL = opts.get(ruby.newSymbol("default_ttl")).toString();
|
77
|
-
}
|
78
|
-
if (opts.containsKey(ruby.newSymbol("transcoder"))) {
|
79
|
-
transcoderValue = opts.get(ruby.newSymbol("transcoder")).toString();
|
80
|
-
}
|
81
|
-
}
|
82
|
-
|
83
|
-
if (distributionValue == null) {
|
84
|
-
distributionValue = "ketama";
|
59
|
+
List<String> servers = new ArrayList<String>();
|
60
|
+
if (args.length > 0) {
|
61
|
+
if (args[0] instanceof RubyString) {
|
62
|
+
servers.add(args[0].toString());
|
63
|
+
} else if (args[0] instanceof RubyArray) {
|
64
|
+
servers.addAll((List<String>) args[0].convertToArray());
|
85
65
|
}
|
86
|
-
if ("array_mod".equals(distributionValue)) {
|
87
|
-
builder.setLocatorType(Locator.ARRAY_MOD);
|
88
|
-
} else if ("ketama".equals(distributionValue) || "consistent_ketama".equals(distributionValue)) {
|
89
|
-
builder.setLocatorType(Locator.CONSISTENT);
|
90
|
-
} else {
|
91
|
-
throw newNotSupport(ruby, "distribution not support");
|
92
|
-
}
|
93
|
-
|
94
|
-
if (hashValue == null) {
|
95
|
-
hashValue = "fnv1_32";
|
96
|
-
}
|
97
|
-
if ("native".equals(hashValue)) {
|
98
|
-
builder.setHashAlg(DefaultHashAlgorithm.NATIVE_HASH);
|
99
|
-
} else if ("crc".equals(hashValue)) {
|
100
|
-
builder.setHashAlg(DefaultHashAlgorithm.CRC_HASH);
|
101
|
-
} else if ("fnv1_64".equals(hashValue)) {
|
102
|
-
builder.setHashAlg(DefaultHashAlgorithm.FNV1_64_HASH);
|
103
|
-
} else if ("fnv1a_64".equals(hashValue)) {
|
104
|
-
builder.setHashAlg(DefaultHashAlgorithm.FNV1A_64_HASH);
|
105
|
-
} else if ("fnv1_32".equals(hashValue)) {
|
106
|
-
builder.setHashAlg(DefaultHashAlgorithm.FNV1_32_HASH);
|
107
|
-
} else if ("fnv1a_32".equals(hashValue)) {
|
108
|
-
builder.setHashAlg(DefaultHashAlgorithm.FNV1A_32_HASH);
|
109
|
-
} else if ("ketama".equals(hashValue)) {
|
110
|
-
builder.setHashAlg(DefaultHashAlgorithm.KETAMA_HASH);
|
111
|
-
} else {
|
112
|
-
throw newNotSupport(ruby, "hash not support");
|
113
|
-
}
|
114
|
-
|
115
|
-
if ("true".equals(binaryValue)) {
|
116
|
-
builder.setProtocol(Protocol.BINARY);
|
117
|
-
}
|
118
|
-
|
119
|
-
if (defaultTTL == null) {
|
120
|
-
ttl = 604800;
|
121
|
-
} else {
|
122
|
-
ttl = Integer.parseInt(defaultTTL);
|
123
|
-
}
|
124
|
-
|
125
|
-
client = new MemcachedClient(builder.build(), addresses);
|
126
|
-
|
127
|
-
if ("marshal_zlib".equals(transcoderValue)) {
|
128
|
-
transcoder = new MarshalZlibTranscoder(ruby);
|
129
|
-
} else {
|
130
|
-
transcoder = new MarshalTranscoder(ruby);
|
131
|
-
}
|
132
|
-
} catch (IOException ioe) {
|
133
|
-
throw context.runtime.newIOErrorFromException(ioe);
|
134
66
|
}
|
135
|
-
|
136
|
-
|
67
|
+
if (servers.isEmpty()) {
|
68
|
+
servers.add("127.0.0.1:11211");
|
69
|
+
}
|
70
|
+
return init(context, servers, options);
|
137
71
|
}
|
138
72
|
|
139
73
|
@JRubyMethod
|
140
74
|
public IRubyObject servers(ThreadContext context) {
|
75
|
+
Ruby ruby = context.getRuntime();
|
141
76
|
List<IRubyObject> addresses = new ArrayList<IRubyObject>();
|
142
77
|
for (SocketAddress address : client.getAvailableServers()) {
|
143
78
|
String addressStr = address.toString();
|
@@ -149,136 +84,139 @@ public class Memcached extends RubyObject {
|
|
149
84
|
return ruby.newArray(addresses);
|
150
85
|
}
|
151
86
|
|
152
|
-
@JRubyMethod
|
153
|
-
public IRubyObject add(ThreadContext context, IRubyObject
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
public IRubyObject add(ThreadContext context, IRubyObject key, IRubyObject value, IRubyObject timeout) {
|
87
|
+
@JRubyMethod(name = "add", required = 2, optional = 3)
|
88
|
+
public IRubyObject add(ThreadContext context, IRubyObject[] args) {
|
89
|
+
Ruby ruby = context.getRuntime();
|
90
|
+
String key = getFullKey(args[0].toString());
|
91
|
+
IRubyObject value = args[1];
|
92
|
+
int timeout = getTimeout(args);
|
159
93
|
try {
|
160
|
-
boolean result = client.add(key
|
94
|
+
boolean result = client.add(key, timeout, value, transcoder).get();
|
161
95
|
if (result == false) {
|
162
|
-
throw newNotStored(ruby, "not stored");
|
96
|
+
throw Error.newNotStored(ruby, "not stored");
|
163
97
|
}
|
164
98
|
return context.nil;
|
165
99
|
} catch (ExecutionException ee) {
|
166
|
-
throw
|
100
|
+
throw ruby.newRuntimeError(ee.getLocalizedMessage());
|
167
101
|
} catch (InterruptedException ie) {
|
168
|
-
throw
|
102
|
+
throw ruby.newThreadError(ie.getLocalizedMessage());
|
169
103
|
}
|
170
104
|
}
|
171
105
|
|
172
|
-
@JRubyMethod
|
173
|
-
public IRubyObject replace(ThreadContext context, IRubyObject
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
public IRubyObject replace(ThreadContext context, IRubyObject key, IRubyObject value, IRubyObject timeout) {
|
106
|
+
@JRubyMethod(name = "replace", required = 2, optional = 3)
|
107
|
+
public IRubyObject replace(ThreadContext context, IRubyObject [] args) {
|
108
|
+
Ruby ruby = context.getRuntime();
|
109
|
+
String key = getFullKey(args[0].toString());
|
110
|
+
IRubyObject value = args[1];
|
111
|
+
int timeout = getTimeout(args);
|
179
112
|
try {
|
180
|
-
boolean result = client.replace(key
|
113
|
+
boolean result = client.replace(key, timeout, value, transcoder).get();
|
181
114
|
if (result == false) {
|
182
|
-
throw newNotStored(ruby, "not stored");
|
115
|
+
throw Error.newNotStored(ruby, "not stored");
|
183
116
|
}
|
184
117
|
return context.nil;
|
185
118
|
} catch (ExecutionException ee) {
|
186
|
-
throw
|
119
|
+
throw ruby.newRuntimeError(ee.getLocalizedMessage());
|
187
120
|
} catch (InterruptedException ie) {
|
188
|
-
throw
|
121
|
+
throw ruby.newThreadError(ie.getLocalizedMessage());
|
189
122
|
}
|
190
123
|
}
|
191
124
|
|
192
|
-
@JRubyMethod
|
193
|
-
public IRubyObject set(ThreadContext context, IRubyObject
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
public IRubyObject set(ThreadContext context, IRubyObject key, IRubyObject value, IRubyObject timeout) {
|
125
|
+
@JRubyMethod(name = "set", required = 2, optional = 3)
|
126
|
+
public IRubyObject set(ThreadContext context, IRubyObject[] args) {
|
127
|
+
Ruby ruby = context.getRuntime();
|
128
|
+
String key = getFullKey(args[0].toString());
|
129
|
+
IRubyObject value = args[1];
|
130
|
+
int timeout = getTimeout(args);
|
199
131
|
try {
|
200
|
-
boolean result = client.set(key
|
132
|
+
boolean result = client.set(key, timeout, value, transcoder).get();
|
201
133
|
if (result == false) {
|
202
|
-
throw newNotStored(ruby, "not stored");
|
134
|
+
throw Error.newNotStored(ruby, "not stored");
|
203
135
|
}
|
204
136
|
return context.nil;
|
205
137
|
} catch (ExecutionException ee) {
|
206
|
-
throw
|
138
|
+
throw ruby.newRuntimeError(ee.getLocalizedMessage());
|
207
139
|
} catch (InterruptedException ie) {
|
208
|
-
throw
|
140
|
+
throw ruby.newThreadError(ie.getLocalizedMessage());
|
209
141
|
}
|
210
142
|
}
|
211
143
|
|
212
|
-
@JRubyMethod
|
213
|
-
public IRubyObject get(ThreadContext context, IRubyObject
|
214
|
-
|
215
|
-
|
216
|
-
|
144
|
+
@JRubyMethod(name = "get", required = 1, optional = 1)
|
145
|
+
public IRubyObject get(ThreadContext context, IRubyObject[] args) {
|
146
|
+
Ruby ruby = context.getRuntime();
|
147
|
+
IRubyObject keys = args[0];
|
148
|
+
if (keys instanceof RubyString) {
|
149
|
+
IRubyObject value = client.get(getFullKey(keys.toString()), transcoder);
|
150
|
+
if (value == null) {
|
151
|
+
throw Error.newNotFound(ruby, "not found");
|
152
|
+
}
|
153
|
+
return value;
|
154
|
+
} else if (keys instanceof RubyArray) {
|
155
|
+
RubyHash results = RubyHash.newHash(ruby);
|
156
|
+
|
157
|
+
Map<String, IRubyObject> bulkResults = client.getBulk(getFullKeys(keys.convertToArray()), transcoder);
|
158
|
+
for (String key : (List<String>) keys.convertToArray()) {
|
159
|
+
if (bulkResults.containsKey(getFullKey(key))) {
|
160
|
+
results.put(key, bulkResults.get(getFullKey(key)));
|
161
|
+
}
|
162
|
+
}
|
163
|
+
return results;
|
217
164
|
}
|
218
|
-
return
|
219
|
-
}
|
220
|
-
|
221
|
-
@JRubyMethod
|
222
|
-
public IRubyObject incr(ThreadContext context, IRubyObject key) {
|
223
|
-
return incr(context, key, ruby.newFixnum(1), ruby.newFixnum(ttl));
|
224
|
-
}
|
225
|
-
|
226
|
-
@JRubyMethod
|
227
|
-
public IRubyObject incr(ThreadContext context, IRubyObject key, IRubyObject by) {
|
228
|
-
return incr(context, key, by, ruby.newFixnum(ttl));
|
165
|
+
return context.nil;
|
229
166
|
}
|
230
167
|
|
231
|
-
@JRubyMethod
|
232
|
-
public IRubyObject incr(ThreadContext context, IRubyObject
|
233
|
-
|
168
|
+
@JRubyMethod(name = "incr", required = 1, optional = 2)
|
169
|
+
public IRubyObject incr(ThreadContext context, IRubyObject[] args) {
|
170
|
+
Ruby ruby = context.getRuntime();
|
171
|
+
String key = getFullKey(args[0].toString());
|
172
|
+
int by = getIncrDecrBy(args);
|
173
|
+
int timeout = getTimeout(args);
|
174
|
+
long result = client.incr(key, by, 1, timeout);
|
234
175
|
return ruby.newFixnum(result);
|
235
176
|
}
|
236
177
|
|
237
|
-
@JRubyMethod
|
238
|
-
public IRubyObject decr(ThreadContext context, IRubyObject
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
return decr(context, key, by, ruby.newFixnum(ttl));
|
245
|
-
}
|
246
|
-
|
247
|
-
@JRubyMethod
|
248
|
-
public IRubyObject decr(ThreadContext context, IRubyObject key, IRubyObject by, IRubyObject timeout) {
|
249
|
-
long result = client.decr(key.toString(), (int)by.convertToInteger().getLongValue(), 0, (int)timeout.convertToInteger().getLongValue());
|
178
|
+
@JRubyMethod(name = "decr", required = 1, optional = 2)
|
179
|
+
public IRubyObject decr(ThreadContext context, IRubyObject[] args) {
|
180
|
+
Ruby ruby = context.getRuntime();
|
181
|
+
String key = getFullKey(args[0].toString());
|
182
|
+
int by = getIncrDecrBy(args);
|
183
|
+
int timeout = getTimeout(args);
|
184
|
+
long result = client.decr(key, by, 0, timeout);
|
250
185
|
return ruby.newFixnum(result);
|
251
186
|
}
|
252
187
|
|
253
188
|
@JRubyMethod
|
254
189
|
public IRubyObject delete(ThreadContext context, IRubyObject key) {
|
190
|
+
Ruby ruby = context.getRuntime();
|
255
191
|
try {
|
256
|
-
boolean result = client.delete(key.toString()).get();
|
192
|
+
boolean result = client.delete(getFullKey(key.toString())).get();
|
257
193
|
if (result == false) {
|
258
|
-
throw newNotFound(ruby, "not found");
|
194
|
+
throw Error.newNotFound(ruby, "not found");
|
259
195
|
}
|
260
196
|
return context.nil;
|
261
197
|
} catch (ExecutionException ee) {
|
262
|
-
throw
|
198
|
+
throw ruby.newRuntimeError(ee.getLocalizedMessage());
|
263
199
|
} catch (InterruptedException ie) {
|
264
|
-
throw
|
200
|
+
throw ruby.newThreadError(ie.getLocalizedMessage());
|
265
201
|
}
|
266
202
|
}
|
267
203
|
|
268
204
|
@JRubyMethod
|
269
205
|
public IRubyObject flush(ThreadContext context) {
|
206
|
+
Ruby ruby = context.getRuntime();
|
270
207
|
try {
|
271
208
|
client.flush().get();
|
272
209
|
return context.nil;
|
273
210
|
} catch (ExecutionException ee) {
|
274
|
-
throw
|
211
|
+
throw ruby.newRuntimeError(ee.getLocalizedMessage());
|
275
212
|
} catch (InterruptedException ie) {
|
276
|
-
throw
|
213
|
+
throw ruby.newThreadError(ie.getLocalizedMessage());
|
277
214
|
}
|
278
215
|
}
|
279
216
|
|
280
217
|
@JRubyMethod
|
281
218
|
public IRubyObject stats(ThreadContext context) {
|
219
|
+
Ruby ruby = context.getRuntime();
|
282
220
|
RubyHash results = RubyHash.newHash(ruby);
|
283
221
|
for(Map.Entry<SocketAddress, Map<String, String>> entry : client.getStats().entrySet()) {
|
284
222
|
RubyHash serverHash = RubyHash.newHash(ruby);
|
@@ -290,36 +228,119 @@ public class Memcached extends RubyObject {
|
|
290
228
|
return results;
|
291
229
|
}
|
292
230
|
|
293
|
-
@JRubyMethod
|
231
|
+
@JRubyMethod(name = {"quit", "shutdown"})
|
294
232
|
public IRubyObject shutdown(ThreadContext context) {
|
295
233
|
client.shutdown();
|
296
234
|
|
297
235
|
return context.nil;
|
298
236
|
}
|
299
237
|
|
300
|
-
|
301
|
-
|
302
|
-
@JRubyClass(name="Memcached::NotFound", parent="Memcached::Error")
|
303
|
-
public static class NotFound extends Error {}
|
304
|
-
@JRubyClass(name="Memcached::NotStored", parent="Memcached::Error")
|
305
|
-
public static class NotStored extends Error {}
|
306
|
-
@JRubyClass(name="Memcached::NotSupport", parent="Memcached::Error")
|
307
|
-
public static class NotSupport extends Error {}
|
308
|
-
|
309
|
-
static RaiseException newNotFound(Ruby ruby, String message) {
|
310
|
-
return newMemcachedError(ruby, "NotFound", message);
|
238
|
+
protected int getDefaultTTL() {
|
239
|
+
return ttl;
|
311
240
|
}
|
312
241
|
|
313
|
-
|
314
|
-
|
242
|
+
protected IRubyObject init(ThreadContext context, List<String> servers, RubyHash options) {
|
243
|
+
Ruby ruby = context.getRuntime();
|
244
|
+
List<InetSocketAddress> addresses = AddrUtil.getAddresses(servers);
|
245
|
+
try {
|
246
|
+
ConnectionFactoryBuilder builder = new ConnectionFactoryBuilder();
|
247
|
+
|
248
|
+
String distributionValue = "ketama";
|
249
|
+
String hashValue = "fnv1_32";
|
250
|
+
RubyBoolean binaryValue = ruby.getFalse();
|
251
|
+
String transcoderValue = null;
|
252
|
+
if (!options.isEmpty()) {
|
253
|
+
RubyHash opts = options.convertToHash();
|
254
|
+
if (opts.containsKey(ruby.newSymbol("distribution"))) {
|
255
|
+
distributionValue = opts.get(ruby.newSymbol("distribution")).toString();
|
256
|
+
}
|
257
|
+
if (opts.containsKey(ruby.newSymbol("hash"))) {
|
258
|
+
hashValue = opts.get(ruby.newSymbol("hash")).toString();
|
259
|
+
}
|
260
|
+
if (opts.containsKey(ruby.newSymbol("binary_protocol"))) {
|
261
|
+
binaryValue = (RubyBoolean) opts.get(ruby.newSymbol("binary_protocol"));
|
262
|
+
}
|
263
|
+
if (opts.containsKey(ruby.newSymbol("default_ttl"))) {
|
264
|
+
ttl = Integer.parseInt(opts.get(ruby.newSymbol("default_ttl")).toString());
|
265
|
+
}
|
266
|
+
if (opts.containsKey(ruby.newSymbol("namespace"))) {
|
267
|
+
prefixKey = opts.get(ruby.newSymbol("namespace")).toString();
|
268
|
+
}
|
269
|
+
if (opts.containsKey(ruby.newSymbol("prefix_key"))) {
|
270
|
+
prefixKey = opts.get(ruby.newSymbol("prefix_key")).toString();
|
271
|
+
}
|
272
|
+
if (opts.containsKey(ruby.newSymbol("transcoder"))) {
|
273
|
+
transcoderValue = opts.get(ruby.newSymbol("transcoder")).toString();
|
274
|
+
}
|
275
|
+
}
|
276
|
+
|
277
|
+
if ("array_mod".equals(distributionValue)) {
|
278
|
+
builder.setLocatorType(Locator.ARRAY_MOD);
|
279
|
+
} else if ("ketama".equals(distributionValue) || "consistent_ketama".equals(distributionValue)) {
|
280
|
+
builder.setLocatorType(Locator.CONSISTENT);
|
281
|
+
} else {
|
282
|
+
throw Error.newNotSupport(ruby, "distribution not support");
|
283
|
+
}
|
284
|
+
if ("native".equals(hashValue)) {
|
285
|
+
builder.setHashAlg(DefaultHashAlgorithm.NATIVE_HASH);
|
286
|
+
} else if ("crc".equals(hashValue)) {
|
287
|
+
builder.setHashAlg(DefaultHashAlgorithm.CRC_HASH);
|
288
|
+
} else if ("fnv1_64".equals(hashValue)) {
|
289
|
+
builder.setHashAlg(DefaultHashAlgorithm.FNV1_64_HASH);
|
290
|
+
} else if ("fnv1a_64".equals(hashValue)) {
|
291
|
+
builder.setHashAlg(DefaultHashAlgorithm.FNV1A_64_HASH);
|
292
|
+
} else if ("fnv1_32".equals(hashValue)) {
|
293
|
+
builder.setHashAlg(DefaultHashAlgorithm.FNV1_32_HASH);
|
294
|
+
} else if ("fnv1a_32".equals(hashValue)) {
|
295
|
+
builder.setHashAlg(DefaultHashAlgorithm.FNV1A_32_HASH);
|
296
|
+
} else if ("ketama".equals(hashValue)) {
|
297
|
+
builder.setHashAlg(DefaultHashAlgorithm.KETAMA_HASH);
|
298
|
+
} else {
|
299
|
+
throw Error.newNotSupport(ruby, "hash not support");
|
300
|
+
}
|
301
|
+
|
302
|
+
if (ruby.getTrue() == binaryValue) {
|
303
|
+
builder.setProtocol(Protocol.BINARY);
|
304
|
+
}
|
305
|
+
|
306
|
+
builder.setDaemon(true);
|
307
|
+
client = new MemcachedClient(builder.build(), addresses);
|
308
|
+
|
309
|
+
if ("marshal_zlib".equals(transcoderValue)) {
|
310
|
+
transcoder = new MarshalZlibTranscoder(ruby);
|
311
|
+
} else {
|
312
|
+
transcoder = new MarshalTranscoder(ruby);
|
313
|
+
}
|
314
|
+
} catch (IOException ioe) {
|
315
|
+
throw ruby.newIOErrorFromException(ioe);
|
316
|
+
}
|
317
|
+
|
318
|
+
return context.nil;
|
319
|
+
}
|
320
|
+
|
321
|
+
private int getTimeout(IRubyObject[] args) {
|
322
|
+
if (args.length > 2) {
|
323
|
+
return (int) args[2].convertToInteger().getLongValue();
|
324
|
+
}
|
325
|
+
return ttl;
|
315
326
|
}
|
316
327
|
|
317
|
-
|
318
|
-
|
328
|
+
private int getIncrDecrBy(IRubyObject[] args) {
|
329
|
+
if (args.length > 1) {
|
330
|
+
return (int) args[1].convertToInteger().getLongValue();
|
331
|
+
}
|
332
|
+
return 1;
|
333
|
+
}
|
334
|
+
|
335
|
+
private List<String> getFullKeys(List<String> keys) {
|
336
|
+
List<String> fullKeys = new ArrayList<String>();
|
337
|
+
for (String key : keys) {
|
338
|
+
fullKeys.add(getFullKey(key));
|
339
|
+
}
|
340
|
+
return fullKeys;
|
319
341
|
}
|
320
342
|
|
321
|
-
private
|
322
|
-
|
323
|
-
return new RaiseException(RubyException.newException(ruby, errorClass, message), true);
|
343
|
+
private String getFullKey(String key) {
|
344
|
+
return prefixKey + key;
|
324
345
|
}
|
325
346
|
}
|