jruby-memcached 0.2.0 → 0.3.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/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
- }