jruby-memcached 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- jruby-memcached (0.2.0)
4
+ jruby-memcached (0.3.0)
5
5
 
6
6
  GEM
7
7
  remote: http://rubygems.org/
data/benchmark.rb CHANGED
@@ -2,15 +2,17 @@ require 'benchmark'
2
2
 
3
3
  JRUBY = defined?(JRUBY_VERSION)
4
4
 
5
+ require 'rubygems'
5
6
  if JRUBY
6
7
  require 'lib/memcached'
8
+ require 'jruby-spymemcached'
7
9
  else
8
10
  require 'memcached'
9
11
  end
10
- require 'rubygems'
11
12
  require 'dalli'
12
13
 
13
14
  memcached = Memcached.new(['localhost:11211'])
15
+ spymemcached = Spymemcached.new(['localhost:11211']) if JRUBY
14
16
  dalli = Dalli::Client.new(['localhost:11211'])
15
17
 
16
18
  3.to_i.times {
@@ -22,6 +24,12 @@ dalli = Dalli::Client.new(['localhost:11211'])
22
24
  bm.report("jruby-memcached get") {
23
25
  100_000.times { memcached.get('foo') }
24
26
  }
27
+ bm.report("jruby-spymemcached set") {
28
+ 100_000.times { spymemcached.set('foo', 'bar') }
29
+ }
30
+ bm.report("jruby-spymemcached get") {
31
+ 100_000.times { spymemcached.get('foo') }
32
+ }
25
33
  else
26
34
  bm.report("memcached set") {
27
35
  100_000.times { memcached.set('foo', 'bar') }
@@ -44,29 +52,46 @@ dalli = Dalli::Client.new(['localhost:11211'])
44
52
  }
45
53
  }
46
54
 
47
- memcached.close
55
+ if JRUBY
56
+ memcached.shutdown
57
+ spymemcached.shutdown
58
+ end
48
59
  dalli.close
49
60
 
50
-
51
- # MBP 2.8G i7
61
+ # I run benchmark for each client 3 times, but only list the last result for each.
62
+ #
63
+ # MBP 2.8G i7 jruby-memcached 0.3.0
52
64
  #
53
65
  # ruby-1.9.3-p194
54
66
  # ruby benchmark.rb
55
67
  # user system total real
56
- # memcached set 1.110000 1.010000 2.120000 ( 4.578121)
57
- # memcached get 0.940000 0.960000 1.900000 ( 4.013941)
58
- # user system total real
59
- # memcached set 1.100000 1.010000 2.110000 ( 4.557462)
60
- # memcached get 0.930000 0.950000 1.880000 ( 3.995192)
61
- # user system total real
62
68
  # memcached set 1.110000 1.020000 2.130000 ( 4.592509)
63
69
  # memcached get 0.970000 1.000000 1.970000 ( 4.172170)
64
70
  # user system total real
65
- # dalli set 8.330000 1.570000 9.900000 ( 10.062159)
66
- # dalli get 8.240000 1.630000 9.870000 ( 9.987921)
71
+ # dalli set 8.360000 1.650000 10.010000 ( 10.193101)
72
+ # dalli get 8.040000 1.670000 9.710000 ( 9.828392)
73
+ #
74
+ # jruby-1.6.7.2
75
+ # jruby --server -Ilib -S benchmark.rb
76
+ # user system total real
77
+ # jruby-memcached set 5.842000 0.000000 5.842000 ( 5.842000)
78
+ # jruby-memcached get 5.561000 0.000000 5.561000 ( 5.561000)
79
+ # user system total real
80
+ # jruby-spymemcached set 5.919000 0.000000 5.919000 ( 5.919000)
81
+ # jruby-spymemcached get 5.615000 0.000000 5.615000 ( 5.615000)
82
+ # user system total real
83
+ # dalli set 10.132000 0.000000 10.132000 ( 10.132000)
84
+ # dalli get 10.600000 0.000000 10.600000 ( 10.600000)
85
+ #
86
+ ##############################################################################
87
+ #
88
+ # MBP 2.8G i7 jruby-memcached 0.1.0
89
+ #
90
+ # ruby-1.9.3-p194
91
+ # ruby benchmark.rb
67
92
  # user system total real
68
- # dalli set 8.400000 1.580000 9.980000 ( 10.139169)
69
- # dalli get 8.500000 1.680000 10.180000 ( 10.287153)
93
+ # memcached set 1.110000 1.020000 2.130000 ( 4.592509)
94
+ # memcached get 0.970000 1.000000 1.970000 ( 4.172170)
70
95
  # user system total real
71
96
  # dalli set 8.330000 1.560000 9.890000 ( 10.094499)
72
97
  # dalli get 8.530000 1.680000 10.210000 ( 10.331083)
@@ -74,20 +99,8 @@ dalli.close
74
99
  # jruby-1.6.7.2
75
100
  # jruby --server -Ilib -S benchmark.rb
76
101
  # user system total real
77
- # jruby-memcached set 8.745000 0.000000 8.745000 ( 8.745000)
78
- # jruby-memcached get 8.260000 0.000000 8.260000 ( 8.260000)
79
- # user system total real
80
- # jruby-memcached set 6.911000 0.000000 6.911000 ( 6.911000)
81
- # jruby-memcached get 6.895000 0.000000 6.895000 ( 6.895000)
82
- # user system total real
83
102
  # jruby-memcached set 6.902000 0.000000 6.902000 ( 6.902000)
84
103
  # jruby-memcached get 6.845000 0.000000 6.845000 ( 6.845000)
85
104
  # user system total real
86
- # dalli set 15.233000 0.000000 15.233000 ( 15.234000)
87
- # dalli get 13.991000 0.000000 13.991000 ( 13.992000)
88
- # user system total real
89
- # dalli set 12.936000 0.000000 12.936000 ( 12.936000)
90
- # dalli get 13.585000 0.000000 13.585000 ( 13.585000)
91
- # user system total real
92
105
  # dalli set 13.251000 0.000000 13.251000 ( 13.251000)
93
106
  # dalli get 13.536000 0.000000 13.536000 ( 13.536000)
data/lib/memcached.rb CHANGED
@@ -1,133 +1,5 @@
1
1
  require 'java'
2
2
  require 'memcached/version'
3
- require 'memcached/exceptions'
4
3
  require File.join(File.dirname(__FILE__), '../target/spymemcached-ext-0.0.1.jar')
5
4
 
6
- java_import 'net.spy.memcached.MemcachedClient'
7
- java_import 'net.spy.memcached.ConnectionFactoryBuilder'
8
- java_import 'net.spy.memcached.ConnectionFactoryBuilder$Locator'
9
- java_import 'net.spy.memcached.ConnectionFactoryBuilder$Protocol'
10
- java_import 'net.spy.memcached.DefaultHashAlgorithm'
11
- java_import 'net.spy.memcached.transcoders.SimpleTranscoder'
12
- java_import 'net.spy.memcached.AddrUtil'
13
-
14
- class Memcached
15
-
16
- FLAGS = 0x0
17
- DEFAULTS = {
18
- :binary_protocol => false,
19
- :default_ttl => 604800,
20
- :distribution => :consistent_ketama,
21
- :exception_retry_limit => 5,
22
- :hash => :fnv1_32,
23
- }
24
-
25
- attr_reader :options
26
- attr_reader :default_ttl
27
-
28
- def initialize(addresses, options={})
29
- @options = DEFAULTS.merge(options)
30
- @default_ttl = @options[:default_ttl]
31
-
32
- builder = ConnectionFactoryBuilder.new
33
-
34
- @options[:distribution] = :consistent if @options[:distribution] == :ketama || @options[:distribution] == :consistent_ketama
35
- unless [:array_mod, :consistent].include? @options[:distribution]
36
- raise Memcached::NotSupport.new("#{@options[:distribution]} distribution algorithm doesn't support yet.")
37
- end
38
- distribution_algorithm = Locator.const_get @options[:distribution].to_s.upcase
39
- builder.setLocatorType(distribution_algorithm)
40
-
41
- if @options[:binary_protocol]
42
- builder.setProtocol(Protocol::BINARY)
43
- end
44
-
45
- unless [:native, :crc, :fnv1_64, :fnv1a_64, :fnv1_32, :fnv1a_32, :ketama].include? @options[:hash]
46
- raise Memcached::NotSupport.new("#{@options[:hash]} hash algorithm doesn't support yet.")
47
- end
48
- hash_algorithm = DefaultHashAlgorithm.const_get "#{@options[:hash]}_HASH".upcase
49
- builder.setHashAlg hash_algorithm
50
-
51
- @client = MemcachedClient.new builder.build, AddrUtil.getAddresses(Array(addresses).join(' '))
52
-
53
- @simple_transcoder = SimpleTranscoder.new
54
- end
55
-
56
- def set(key, value, ttl=@default_ttl, marshal=true, flags=FLAGS)
57
- with_retry do
58
- value = encode(value, marshal, flags)
59
- @simple_transcoder.setFlags(flags)
60
- @client.set(key, ttl, value.to_java_bytes, @simple_transcoder).get
61
- end
62
- end
63
-
64
- def add(key, value, ttl=@default_ttl, marshal=true, flags=FLAGS)
65
- with_retry do
66
- value = encode(value, marshal, flags)
67
- @simple_transcoder.setFlags(flags)
68
- if @client.add(key, ttl, value.to_java_bytes, @simple_transcoder).get === false
69
- raise Memcached::NotStored
70
- end
71
- end
72
- end
73
-
74
- def replace(key, value, ttl=@default_ttl, marshal=true, flags=FLAGS)
75
- with_retry do
76
- value = encode(value, marshal, flags)
77
- @simple_transcoder.setFlags(flags)
78
- if @client.replace(key, ttl, value.to_java_bytes, @simple_transcoder).get === false
79
- raise Memcached::NotStored
80
- end
81
- end
82
- end
83
-
84
- def delete(key)
85
- with_retry do
86
- raise Memcached::NotFound if @client.delete(key).get === false
87
- end
88
- end
89
-
90
- def get(key, marshal=true)
91
- with_retry do
92
- ret = @client.get(key, @simple_transcoder)
93
- raise Memcached::NotFound if ret.nil?
94
- flags, data = ret.flags, ret.data
95
- value = String.from_java_bytes data
96
- value = decode(value, marshal, flags)
97
- end
98
- end
99
-
100
- def flush
101
- @client.flush.get
102
- end
103
-
104
- def servers
105
- @client.available_servers.map { |address| address.to_s.split("/").last }
106
- end
107
-
108
- def close
109
- @client.shutdown
110
- end
111
-
112
- alias_method :quit, :close
113
-
114
- private
115
- def with_retry
116
- begin
117
- yield
118
- rescue
119
- tries ||= 0
120
- raise unless tries < options[:exception_retry_limit]
121
- tries += 1
122
- retry
123
- end
124
- end
125
-
126
- def encode(value, marshal, flags)
127
- marshal ? Marshal.dump(value) : value
128
- end
129
-
130
- def decode(value, marshal, flags)
131
- marshal ? Marshal.load(value) : value
132
- end
133
- end
5
+ com.openfeint.memcached.MemcachedLibrary.new.load(JRuby.runtime, false)
@@ -1,3 +1,3 @@
1
1
  class Memcached
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
data/pom.xml CHANGED
@@ -44,7 +44,7 @@
44
44
  <plugin>
45
45
  <groupId>org.apache.maven.plugins</groupId>
46
46
  <artifactId>maven-shade-plugin</artifactId>
47
- <version>1.4</version>
47
+ <version>1.6</version>
48
48
  <executions>
49
49
  <execution>
50
50
  <phase>package</phase>
@@ -1,30 +1,16 @@
1
1
  require 'spec_helper'
2
2
 
3
+ com.openfeint.memcached.MemcachedLibrary.new.load(JRuby.runtime, false)
3
4
  describe Memcached do
4
5
  context "localhost" do
5
- before(:all) { @memcached = Memcached.new("127.0.0.1:11211") }
6
- after(:all) { @memcached.close }
6
+ before(:all) { @memcached = Memcached.new(["127.0.0.1:11211"]) }
7
+ after(:all) { @memcached.shutdown }
7
8
 
8
9
  it "should get all servers" do
9
10
  @memcached.servers.should == ["127.0.0.1:11211"]
10
11
  end
11
12
 
12
13
  context "initialize" do
13
- before(:all) do
14
- @memcached_with_options = Memcached.new("127.0.0.1:11211", :hash => :fnv1_32,
15
- :distribution => :ketama,
16
- :default_ttl => 900)
17
- end
18
- after(:all) { @memcached_with_options.close }
19
-
20
- it "should with option hash" do
21
- @memcached_with_options.options[:hash].should == :fnv1_32
22
- end
23
-
24
- it "should with option distribution" do
25
- @memcached_with_options.options[:distribution].should == :consistent
26
- end
27
-
28
14
  it "should raise error with unsupported option hash" do
29
15
  lambda { Memcached.new("127.0.0.1:11211", :hash => :unknown) }.should raise_error(Memcached::NotSupport)
30
16
  end
@@ -32,10 +18,6 @@ describe Memcached do
32
18
  it "should raise error with unsupported option distribution" do
33
19
  lambda { Memcached.new("127.0.0.1:11211", :distribution => :unknown) }.should raise_error(Memcached::NotSupport)
34
20
  end
35
-
36
- it "should get default ttl" do
37
- @memcached_with_options.default_ttl.should == 900
38
- end
39
21
  end
40
22
 
41
23
  context "set/get" do
@@ -69,12 +51,6 @@ describe Memcached do
69
51
  sleep 1
70
52
  lambda { @memcached.get("key") }.should raise_error(Memcached::NotFound)
71
53
  end
72
-
73
- it "should retry when set failure" do
74
- Java::NetSpyMemcached::MemcachedClient.any_instance.stubs(:set).raises(Memcached::NotStored)
75
- Java::NetSpyMemcachedTranscoders::SimpleTranscoder.any_instance.expects(:setFlags).times(6)
76
- lambda { @memcached.set "key", "value" }.should raise_error(Memcached::NotStored)
77
- end
78
54
  end
79
55
 
80
56
  context "add" do
@@ -0,0 +1,62 @@
1
+ package com.openfeint.memcached;
2
+
3
+ import java.io.ByteArrayInputStream;
4
+ import java.io.ByteArrayOutputStream;
5
+ import java.io.IOException;
6
+ import net.spy.memcached.CachedData;
7
+ import net.spy.memcached.transcoders.Transcoder;
8
+ import org.jruby.Ruby;
9
+ import org.jruby.runtime.builtin.IRubyObject;
10
+ import org.jruby.runtime.marshal.MarshalStream;
11
+ import org.jruby.runtime.marshal.UnmarshalStream;
12
+
13
+ /**
14
+ *
15
+ * MarshalTranscoder does marshaling and unmarshaling.
16
+ *
17
+ */
18
+ public class MarshalTranscoder implements Transcoder<IRubyObject> {
19
+ private Ruby ruby;
20
+ private int flags;
21
+
22
+ public MarshalTranscoder(Ruby ruby) {
23
+ this(ruby, 0);
24
+ }
25
+
26
+ public MarshalTranscoder(Ruby ruby, int flags) {
27
+ this.ruby = ruby;
28
+ this.flags = flags;
29
+ }
30
+
31
+ public boolean asyncDecode(CachedData d) {
32
+ return false;
33
+ }
34
+
35
+ public CachedData encode(IRubyObject o) {
36
+ try {
37
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
38
+ MarshalStream marshal = new MarshalStream(ruby, baos, Integer.MAX_VALUE);
39
+ marshal.dumpObject(o);
40
+ byte[] bytes = baos.toByteArray();
41
+ return new CachedData(getFlags(), bytes, bytes.length);
42
+ } catch (IOException ioe) {
43
+ throw ruby.newIOErrorFromException(ioe);
44
+ }
45
+ }
46
+
47
+ public IRubyObject decode(CachedData d) {
48
+ try {
49
+ return new UnmarshalStream(ruby, new ByteArrayInputStream(d.getData()), null, false, false).unmarshalObject();
50
+ } catch (IOException ioe) {
51
+ throw ruby.newIOErrorFromException(ioe);
52
+ }
53
+ }
54
+
55
+ public int getMaxSize() {
56
+ return CachedData.MAX_SIZE;
57
+ }
58
+
59
+ public int getFlags() {
60
+ return flags;
61
+ }
62
+ }
@@ -0,0 +1,97 @@
1
+ package com.openfeint.memcached;
2
+
3
+ import java.io.ByteArrayInputStream;
4
+ import java.io.ByteArrayOutputStream;
5
+ import java.io.ObjectInputStream;
6
+ import java.io.ObjectOutputStream;
7
+ import java.io.IOException;
8
+ import net.spy.memcached.CachedData;
9
+ import net.spy.memcached.transcoders.Transcoder;
10
+ import org.jruby.Ruby;
11
+ import org.jruby.runtime.builtin.IRubyObject;
12
+ import org.jruby.runtime.marshal.MarshalStream;
13
+ import org.jruby.runtime.marshal.UnmarshalStream;
14
+ import org.jruby.util.ByteList;
15
+ import com.jcraft.jzlib.ZOutputStream;
16
+ import com.jcraft.jzlib.ZInputStream;
17
+ import com.jcraft.jzlib.JZlib;
18
+
19
+ /**
20
+ *
21
+ * MarshalZlibTranscoder do marshaling/unmarshaling and compressing/decompressing with zlib.
22
+ *
23
+ */
24
+ public class MarshalZlibTranscoder implements Transcoder<IRubyObject> {
25
+ private Ruby ruby;
26
+ private int flags;
27
+
28
+ public MarshalZlibTranscoder(Ruby ruby) {
29
+ this(ruby, 1);
30
+ }
31
+
32
+ public MarshalZlibTranscoder(Ruby ruby, int flags) {
33
+ this.ruby = ruby;
34
+ this.flags = flags;
35
+ }
36
+
37
+ public boolean asyncDecode(CachedData d) {
38
+ return false;
39
+ }
40
+
41
+ public CachedData encode(IRubyObject o) {
42
+ try {
43
+ ByteArrayOutputStream out1 = new ByteArrayOutputStream();
44
+ MarshalStream marshal = new MarshalStream(ruby, out1, Integer.MAX_VALUE);
45
+ marshal.dumpObject(o);
46
+
47
+ byte[] bytes;
48
+ if (flags == 1) {
49
+ ByteArrayOutputStream out2 = new ByteArrayOutputStream();
50
+ ZOutputStream zout = new ZOutputStream(out2, JZlib.Z_DEFAULT_COMPRESSION);
51
+ zout.write(out1.toByteArray());
52
+ zout.close();
53
+ bytes = out2.toByteArray();
54
+ } else {
55
+ bytes = out1.toByteArray();
56
+ }
57
+
58
+ return new CachedData(getFlags(), bytes, bytes.length);
59
+ } catch (IOException ioe) {
60
+ throw ruby.newIOErrorFromException(ioe);
61
+ }
62
+ }
63
+
64
+ public IRubyObject decode(CachedData d) {
65
+ try {
66
+ byte[] bytes;
67
+ if (d.getFlags() == 1) {
68
+ ByteArrayInputStream in = new ByteArrayInputStream(d.getData());
69
+ ZInputStream zin = new ZInputStream(in);
70
+
71
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream();
72
+ int nRead;
73
+ byte[] data = new byte[1024];
74
+ while ((nRead = zin.read(data, 0, data.length)) != -1) {
75
+ buffer.write(data, 0, nRead);
76
+ }
77
+ buffer.flush();
78
+ bytes = buffer.toByteArray();
79
+ zin.close();
80
+ } else {
81
+ bytes = d.getData();
82
+ }
83
+
84
+ return new UnmarshalStream(ruby, new ByteArrayInputStream(bytes), null, false, false).unmarshalObject();
85
+ } catch (IOException ioe) {
86
+ throw ruby.newIOErrorFromException(ioe);
87
+ }
88
+ }
89
+
90
+ public int getMaxSize() {
91
+ return CachedData.MAX_SIZE;
92
+ }
93
+
94
+ public int getFlags() {
95
+ return flags;
96
+ }
97
+ }
@@ -0,0 +1,325 @@
1
+ package com.openfeint.memcached;
2
+
3
+ import java.io.IOException;
4
+ import java.net.InetSocketAddress;
5
+ import java.net.SocketAddress;
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;
11
+ import net.spy.memcached.AddrUtil;
12
+ import net.spy.memcached.CachedData;
13
+ import net.spy.memcached.ConnectionFactoryBuilder;
14
+ import net.spy.memcached.ConnectionFactoryBuilder.Locator;
15
+ import net.spy.memcached.ConnectionFactoryBuilder.Protocol;
16
+ import net.spy.memcached.DefaultHashAlgorithm;
17
+ import net.spy.memcached.MemcachedClient;
18
+ import net.spy.memcached.transcoders.Transcoder;
19
+ import org.jruby.Ruby;
20
+ import org.jruby.RubyClass;
21
+ import org.jruby.RubyException;
22
+ import org.jruby.RubyHash;
23
+ import org.jruby.RubyObject;
24
+ import org.jruby.anno.JRubyClass;
25
+ import org.jruby.anno.JRubyMethod;
26
+ import org.jruby.exceptions.RaiseException;
27
+ import org.jruby.runtime.ThreadContext;
28
+ import org.jruby.runtime.builtin.IRubyObject;
29
+
30
+ public class Memcached extends RubyObject {
31
+ private Ruby ruby;
32
+
33
+ private MemcachedClient client;
34
+
35
+ private Transcoder<IRubyObject> transcoder;
36
+
37
+ private int ttl;
38
+
39
+ public Memcached(final Ruby ruby, RubyClass rubyClass) {
40
+ super(ruby, rubyClass);
41
+ this.ruby = ruby;
42
+ }
43
+
44
+ @JRubyMethod
45
+ public IRubyObject initialize(ThreadContext context, IRubyObject servers) {
46
+ return initialize(context, servers, context.nil);
47
+ }
48
+ @JRubyMethod
49
+ public IRubyObject initialize(ThreadContext context, IRubyObject servers, IRubyObject options) {
50
+ List<InetSocketAddress> addresses;
51
+ if ("java.lang.String".equals(servers.getJavaClass().getName())) {
52
+ addresses = AddrUtil.getAddresses(servers.convertToString().toString());
53
+ } else {
54
+ addresses = AddrUtil.getAddresses((List<String>)servers.convertToArray());
55
+ }
56
+ try {
57
+ ConnectionFactoryBuilder builder = new ConnectionFactoryBuilder();
58
+
59
+ String distributionValue = null;
60
+ String hashValue = null;
61
+ String binaryValue = null;
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";
85
+ }
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
+ }
135
+
136
+ return context.nil;
137
+ }
138
+
139
+ @JRubyMethod
140
+ public IRubyObject servers(ThreadContext context) {
141
+ List<IRubyObject> addresses = new ArrayList<IRubyObject>();
142
+ for (SocketAddress address : client.getAvailableServers()) {
143
+ String addressStr = address.toString();
144
+ if (addressStr.indexOf("/") == 0) {
145
+ addressStr = addressStr.replace("/", "");
146
+ }
147
+ addresses.add(ruby.newString(addressStr));
148
+ }
149
+ return ruby.newArray(addresses);
150
+ }
151
+
152
+ @JRubyMethod
153
+ public IRubyObject add(ThreadContext context, IRubyObject key, IRubyObject value) {
154
+ return add(context, key, value, ruby.newFixnum(ttl));
155
+ }
156
+
157
+ @JRubyMethod
158
+ public IRubyObject add(ThreadContext context, IRubyObject key, IRubyObject value, IRubyObject timeout) {
159
+ try {
160
+ boolean result = client.add(key.toString(), (int)timeout.convertToInteger().getLongValue(), value, transcoder).get();
161
+ if (result == false) {
162
+ throw newNotStored(ruby, "not stored");
163
+ }
164
+ return context.nil;
165
+ } catch (ExecutionException ee) {
166
+ throw context.runtime.newRuntimeError(ee.getLocalizedMessage());
167
+ } catch (InterruptedException ie) {
168
+ throw context.runtime.newThreadError(ie.getLocalizedMessage());
169
+ }
170
+ }
171
+
172
+ @JRubyMethod
173
+ public IRubyObject replace(ThreadContext context, IRubyObject key, IRubyObject value) {
174
+ return replace(context, key, value, ruby.newFixnum(ttl));
175
+ }
176
+
177
+ @JRubyMethod
178
+ public IRubyObject replace(ThreadContext context, IRubyObject key, IRubyObject value, IRubyObject timeout) {
179
+ try {
180
+ boolean result = client.replace(key.toString(), (int)timeout.convertToInteger().getLongValue(), value, transcoder).get();
181
+ if (result == false) {
182
+ throw newNotStored(ruby, "not stored");
183
+ }
184
+ return context.nil;
185
+ } catch (ExecutionException ee) {
186
+ throw context.runtime.newRuntimeError(ee.getLocalizedMessage());
187
+ } catch (InterruptedException ie) {
188
+ throw context.runtime.newThreadError(ie.getLocalizedMessage());
189
+ }
190
+ }
191
+
192
+ @JRubyMethod
193
+ public IRubyObject set(ThreadContext context, IRubyObject key, IRubyObject value) {
194
+ return set(context, key, value, ruby.newFixnum(ttl));
195
+ }
196
+
197
+ @JRubyMethod
198
+ public IRubyObject set(ThreadContext context, IRubyObject key, IRubyObject value, IRubyObject timeout) {
199
+ try {
200
+ boolean result = client.set(key.toString(), (int)timeout.convertToInteger().getLongValue(), value, transcoder).get();
201
+ if (result == false) {
202
+ throw newNotStored(ruby, "not stored");
203
+ }
204
+ return context.nil;
205
+ } catch (ExecutionException ee) {
206
+ throw context.runtime.newRuntimeError(ee.getLocalizedMessage());
207
+ } catch (InterruptedException ie) {
208
+ throw context.runtime.newThreadError(ie.getLocalizedMessage());
209
+ }
210
+ }
211
+
212
+ @JRubyMethod
213
+ public IRubyObject get(ThreadContext context, IRubyObject key) {
214
+ IRubyObject value = client.get(key.toString(), transcoder);
215
+ if (value == null) {
216
+ throw newNotFound(ruby, "not found");
217
+ }
218
+ return value;
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));
229
+ }
230
+
231
+ @JRubyMethod
232
+ public IRubyObject incr(ThreadContext context, IRubyObject key, IRubyObject by, IRubyObject timeout) {
233
+ long result = client.incr(key.toString(), (int)by.convertToInteger().getLongValue(), 1, (int)timeout.convertToInteger().getLongValue());
234
+ return ruby.newFixnum(result);
235
+ }
236
+
237
+ @JRubyMethod
238
+ public IRubyObject decr(ThreadContext context, IRubyObject key) {
239
+ return decr(context, key, ruby.newFixnum(1), ruby.newFixnum(ttl));
240
+ }
241
+
242
+ @JRubyMethod
243
+ public IRubyObject decr(ThreadContext context, IRubyObject key, IRubyObject by) {
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());
250
+ return ruby.newFixnum(result);
251
+ }
252
+
253
+ @JRubyMethod
254
+ public IRubyObject delete(ThreadContext context, IRubyObject key) {
255
+ try {
256
+ boolean result = client.delete(key.toString()).get();
257
+ if (result == false) {
258
+ throw newNotFound(ruby, "not found");
259
+ }
260
+ return context.nil;
261
+ } catch (ExecutionException ee) {
262
+ throw context.runtime.newRuntimeError(ee.getLocalizedMessage());
263
+ } catch (InterruptedException ie) {
264
+ throw context.runtime.newThreadError(ie.getLocalizedMessage());
265
+ }
266
+ }
267
+
268
+ @JRubyMethod
269
+ public IRubyObject flush(ThreadContext context) {
270
+ try {
271
+ client.flush().get();
272
+ return context.nil;
273
+ } catch (ExecutionException ee) {
274
+ throw context.runtime.newRuntimeError(ee.getLocalizedMessage());
275
+ } catch (InterruptedException ie) {
276
+ throw context.runtime.newThreadError(ie.getLocalizedMessage());
277
+ }
278
+ }
279
+
280
+ @JRubyMethod
281
+ public IRubyObject stats(ThreadContext context) {
282
+ RubyHash results = RubyHash.newHash(ruby);
283
+ for(Map.Entry<SocketAddress, Map<String, String>> entry : client.getStats().entrySet()) {
284
+ RubyHash serverHash = RubyHash.newHash(ruby);
285
+ for(Map.Entry<String, String> server : entry.getValue().entrySet()) {
286
+ serverHash.op_aset(context, ruby.newString(server.getKey()), ruby.newString(server.getValue()));
287
+ }
288
+ results.op_aset(context, ruby.newString(entry.getKey().toString()), serverHash);
289
+ }
290
+ return results;
291
+ }
292
+
293
+ @JRubyMethod
294
+ public IRubyObject shutdown(ThreadContext context) {
295
+ client.shutdown();
296
+
297
+ return context.nil;
298
+ }
299
+
300
+ @JRubyClass(name="Memcached::Error", parent="RuntimeError")
301
+ public static class Error {}
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);
311
+ }
312
+
313
+ static RaiseException newNotStored(Ruby ruby, String message) {
314
+ return newMemcachedError(ruby, "NotStored", message);
315
+ }
316
+
317
+ static RaiseException newNotSupport(Ruby ruby, String message) {
318
+ return newMemcachedError(ruby, "NotSupport", message);
319
+ }
320
+
321
+ private static RaiseException newMemcachedError(Ruby ruby, String klass, String message) {
322
+ RubyClass errorClass = ruby.getModule("Memcached").getClass(klass);
323
+ return new RaiseException(RubyException.newException(ruby, errorClass, message), true);
324
+ }
325
+ }
@@ -0,0 +1,52 @@
1
+ package com.openfeint.memcached;
2
+
3
+ import java.io.ByteArrayInputStream;
4
+ import java.io.ByteArrayOutputStream;
5
+ import java.io.IOException;
6
+ import java.net.InetSocketAddress;
7
+ import java.net.SocketAddress;
8
+ import java.util.ArrayList;
9
+ import java.util.List;
10
+ import java.util.Map;
11
+ import java.util.concurrent.ExecutionException;
12
+ import net.spy.memcached.CachedData;
13
+ import net.spy.memcached.MemcachedClient;
14
+ import net.spy.memcached.transcoders.Transcoder;
15
+ import org.jruby.Ruby;
16
+ import org.jruby.RubyClass;
17
+ import org.jruby.RubyException;
18
+ import org.jruby.RubyHash;
19
+ import org.jruby.RubyObject;
20
+ import org.jruby.RubyString;
21
+ import org.jruby.RubySymbol;
22
+ import org.jruby.anno.JRubyClass;
23
+ import org.jruby.anno.JRubyMethod;
24
+ import org.jruby.exceptions.RaiseException;
25
+ import org.jruby.runtime.ObjectAllocator;
26
+ import org.jruby.runtime.ThreadContext;
27
+ import org.jruby.runtime.builtin.IRubyObject;
28
+ import org.jruby.runtime.load.Library;
29
+ import org.jruby.runtime.marshal.MarshalStream;
30
+ import org.jruby.runtime.marshal.UnmarshalStream;
31
+
32
+ public class MemcachedLibrary implements Library {
33
+ private Ruby ruby;
34
+
35
+ public void load(final Ruby ruby, boolean bln) throws IOException {
36
+ this.ruby = ruby;
37
+
38
+ RubyClass memcached = ruby.defineClass("Memcached", ruby.getObject(), new ObjectAllocator() {
39
+ public IRubyObject allocate(Ruby ruby, RubyClass rc) {
40
+ return new Memcached(ruby, rc);
41
+ }
42
+ });
43
+
44
+ memcached.defineAnnotatedMethods(Memcached.class);
45
+
46
+ RubyClass runtimeError = ruby.getRuntimeError();
47
+ RubyClass memcachedError = memcached.defineClassUnder("Error", runtimeError, runtimeError.getAllocator());
48
+ memcached.defineClassUnder("NotFound", memcachedError, memcachedError.getAllocator());
49
+ memcached.defineClassUnder("NotStored", memcachedError, memcachedError.getAllocator());
50
+ memcached.defineClassUnder("NotSupport", memcachedError, memcachedError.getAllocator());
51
+ }
52
+ }
Binary file
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: jruby-memcached
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.2.0
5
+ version: 0.3.0
6
6
  platform: ruby
7
7
  authors:
8
8
  - Richard Huang
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2012-07-29 00:00:00 Z
13
+ date: 2012-08-07 00:00:00 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rspec
@@ -58,9 +58,11 @@ files:
58
58
  - pom.xml
59
59
  - spec/memcached_spec.rb
60
60
  - spec/spec_helper.rb
61
+ - src/main/java/com/openfeint/memcached/MarshalTranscoder.java
62
+ - src/main/java/com/openfeint/memcached/MarshalZlibTranscoder.java
63
+ - src/main/java/com/openfeint/memcached/Memcached.java
64
+ - src/main/java/com/openfeint/memcached/MemcachedLibrary.java
61
65
  - src/main/java/net/spy/memcached/KetamaNodeLocator.java
62
- - src/main/java/net/spy/memcached/ReturnData.java
63
- - src/main/java/net/spy/memcached/transcoders/SimpleTranscoder.java
64
66
  - src/main/java/net/spy/memcached/util/DefaultKetamaNodeLocatorConfiguration.java
65
67
  - target/spymemcached-ext-0.0.1.jar
66
68
  homepage: https://github.com/aurorafeint/jruby-memcached
@@ -86,7 +88,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
86
88
  requirements: []
87
89
 
88
90
  rubyforge_project:
89
- rubygems_version: 1.8.15
91
+ rubygems_version: 1.8.24
90
92
  signing_key:
91
93
  specification_version: 3
92
94
  summary: jruby compatible memcached client
@@ -1,20 +0,0 @@
1
- package net.spy.memcached;
2
-
3
- public class ReturnData {
4
- private int flags;
5
- private byte[] data;
6
-
7
- public ReturnData(int flags, byte[] data) {
8
- this.flags = flags;
9
- this.data = data;
10
- }
11
-
12
-
13
- public int getFlags() {
14
- return flags;
15
- }
16
-
17
- public byte[] getData() {
18
- return data;
19
- }
20
- }
@@ -1,53 +0,0 @@
1
- package net.spy.memcached.transcoders;
2
-
3
- import net.spy.memcached.CachedData;
4
- import net.spy.memcached.ReturnData;
5
- import net.spy.memcached.transcoders.Transcoder;
6
-
7
- /**
8
- *
9
- * SimpleTranscoder didn't do any serializing/deserializing or compression/decompression.
10
- * Ruby will convert object to string by Marshal and finally passing bytes[].
11
- *
12
- */
13
- public class SimpleTranscoder implements Transcoder<Object> {
14
- private final int maxSize;
15
- private int flags;
16
-
17
- public SimpleTranscoder() {
18
- this(CachedData.MAX_SIZE, 0);
19
- }
20
-
21
- public SimpleTranscoder(int flags) {
22
- this(CachedData.MAX_SIZE, flags);
23
- }
24
-
25
- public SimpleTranscoder(int maxSize, int flags) {
26
- this.maxSize = maxSize;
27
- this.flags = flags;
28
- }
29
-
30
- public boolean asyncDecode(CachedData d) {
31
- return false;
32
- }
33
-
34
- public CachedData encode(Object o) {
35
- return new CachedData(getFlags(), (byte[]) o, getMaxSize());
36
- }
37
-
38
- public Object decode(CachedData d) {
39
- return new ReturnData(d.getFlags(), d.getData());
40
- }
41
-
42
- public int getMaxSize() {
43
- return maxSize;
44
- }
45
-
46
- public int getFlags() {
47
- return flags;
48
- }
49
-
50
- public void setFlags(int flags) {
51
- this.flags = flags;
52
- }
53
- }