Permalink
Browse files

Migrating Membase client to Couchbase namespaces

With the 1.8 CBS release, the .NET Couchbase client is being updated
to include proper Couchbase class and namespace naming.  The new Couchbase
project found here originated as a fork of the Membase client
located at https://github.com/enyim/EnyimMemcached.

Commit 0c6fe754b2da57c72a688a7bf071092e0d7c7c8b was the starting point.
The client in this commit is not yet signed as was the Membase client.

Change-Id: I0c2d2bdf47e5639df727ca870ef22e0320cfe89e
Reviewed-on: http://review.couchbase.org/12023
Tested-by: John C. Zablocki <john@couchbase.com>
Reviewed-by: Matt Ingenthron <matt@couchbase.com>
  • Loading branch information...
1 parent 0ed422a commit d53ddcac10f3416f36fa2a676f3ab8872e718920 @johnzablocki johnzablocki committed with ingenthr Jan 3, 2012
Showing with 4,380 additions and 0 deletions.
  1. +11 −0 .gitignore
  2. +3 −0 .gitmodules
  3. +1 −0 lib/EnyimMemcached
  4. +26 −0 src/Couchbase.sln
  5. +45 −0 src/Couchbase/BasicCouchbaseOperationFactory.cs
  6. +276 −0 src/Couchbase/BucketConfigListener.cs
  7. +7 −0 src/Couchbase/Changes.mdown
  8. +28 −0 src/Couchbase/Config.transform
  9. +92 −0 src/Couchbase/ConfigHelper.cs
  10. +40 −0 src/Couchbase/Configuration/BucketPortType.cs
  11. +293 −0 src/Couchbase/Configuration/CouchbaseClientConfiguration.cs
  12. +169 −0 src/Couchbase/Configuration/CouchbaseClientSection.cs
  13. +44 −0 src/Couchbase/Configuration/DefaultPerformanceMonitorFactory.cs
  14. +75 −0 src/Couchbase/Configuration/ICouchbaseClientConfiguration.cs
  15. +33 −0 src/Couchbase/Configuration/ICouchbasePerformanceMonitorFactory.cs
  16. +119 −0 src/Couchbase/Configuration/ServersElement.cs
  17. +42 −0 src/Couchbase/Configuration/UriElement.cs
  18. +68 −0 src/Couchbase/Configuration/UriElementCollection.cs
  19. +85 −0 src/Couchbase/Configuration/UriValidator.cs
  20. +111 −0 src/Couchbase/Couchbase.csproj
  21. +19 −0 src/Couchbase/Couchbase.nuspec
  22. +464 −0 src/Couchbase/CouchbaseClient.cs
  23. +577 −0 src/Couchbase/CouchbasePool.cs
  24. +28 −0 src/Couchbase/Demo.config
  25. +218 −0 src/Couchbase/Deserialization.cs
  26. +15 −0 src/Couchbase/ICouchbaseOperationFactory.cs
  27. +13 −0 src/Couchbase/ICouchbaseServerPool.cs
  28. +564 −0 src/Couchbase/MessageStreamListener.cs
  29. +11 −0 src/Couchbase/OperationInterfaces.cs
  30. +88 −0 src/Couchbase/Operations/GetAndTouchOperation.cs
  31. +240 −0 src/Couchbase/Operations/SyncOperation.cs
  32. +65 −0 src/Couchbase/Operations/TouchOperation.cs
  33. +35 −0 src/Couchbase/Properties/AssemblyInfo.cs
  34. +359 −0 src/Couchbase/VBucketAwareOperationFactory.cs
  35. +116 −0 src/Couchbase/WebClientWithTimeout.cs
  36. BIN src/Couchbase/public_key.snk
View
@@ -0,0 +1,11 @@
+[Oo]bj/
+[Bb]in/
+*.suo
+*.user
+/TestResults
+*.vspscc
+*.vssscc
+*.vsmdi
+*.vs*
+*.testsettings
+*.sln.docstates
View
@@ -0,0 +1,3 @@
+[submodule "lib/EnyimMemcached"]
+ path = lib/EnyimMemcached
+ url = git://github.com/enyim/EnyimMemcached
Submodule EnyimMemcached added at 0c6fe7
View
@@ -0,0 +1,26 @@
+
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual Studio 2010
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Couchbase", "Couchbase\Couchbase.csproj", "{708A2350-A26C-444D-B975-8164263951A7}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Enyim.Caching", "..\lib\EnyimMemcached\Enyim.Caching\Enyim.Caching.csproj", "{D438C0B3-A168-40B8-BDDD-61F0939DFF35}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {708A2350-A26C-444D-B975-8164263951A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {708A2350-A26C-444D-B975-8164263951A7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {708A2350-A26C-444D-B975-8164263951A7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {708A2350-A26C-444D-B975-8164263951A7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D438C0B3-A168-40B8-BDDD-61F0939DFF35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D438C0B3-A168-40B8-BDDD-61F0939DFF35}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D438C0B3-A168-40B8-BDDD-61F0939DFF35}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D438C0B3-A168-40B8-BDDD-61F0939DFF35}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+
+namespace Couchbase
+{
+ internal class BasicCouchbaseOperationFactory : Enyim.Caching.Memcached.Protocol.Binary.BinaryOperationFactory, ICouchbaseOperationFactory
+ {
+ internal static readonly BasicCouchbaseOperationFactory Instance = new BasicCouchbaseOperationFactory();
+
+ ITouchOperation ICouchbaseOperationFactory.Touch(string key, uint newExpiration)
+ {
+ return new TouchOperation(null, key, newExpiration);
+ }
+
+ IGetAndTouchOperation ICouchbaseOperationFactory.GetAndTouch(string key, uint newExpiration)
+ {
+ return new GetAndTouchOperation(null, key, newExpiration);
+ }
+
+ ISyncOperation ICouchbaseOperationFactory.Sync(SyncMode mode, IList<KeyValuePair<string, ulong>> keys, int replicationCount)
+ {
+ throw new NotSupportedException("Sync is not supported on memcached buckets.");
+ }
+ }
+}
+
+#region [ License information ]
+/* ************************************************************
+ *
+ * Copyright (c) 2010 Attila Kiskó, enyim.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ************************************************************/
+#endregion
@@ -0,0 +1,276 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web.Script.Serialization;
+using System.Threading;
+using System.Net;
+using Enyim;
+using Couchbase.Configuration;
+
+namespace Couchbase
+{
+ internal class BucketConfigListener
+ {
+ private static readonly Enyim.Caching.ILog log = Enyim.Caching.LogManager.GetLogger(typeof(BucketConfigListener));
+
+ private Uri[] poolUrls;
+ private string bucketName;
+ private NetworkCredential credential;
+ private int? lastHash;
+ private ManualResetEvent mre;
+ private MessageStreamListener listener;
+
+ public BucketConfigListener(Uri[] poolUrls, string bucketName, string bucketPassword)
+ {
+ this.poolUrls = poolUrls;
+ this.bucketName = String.IsNullOrEmpty(bucketName)
+ ? "default"
+ : bucketName;
+
+ this.credential = bucketName == "default" || String.IsNullOrEmpty(bucketPassword)
+ ? null
+ : new NetworkCredential(bucketName, bucketPassword);
+
+ this.Timeout = 10000;
+ this.DeadTimeout = 10000;
+
+ this.RetryCount = 0;
+ this.RetryTimeout = new TimeSpan(0, 0, 0, 0, 500);
+ }
+
+ /// <summary>
+ /// Connection timeout in milliseconds for connecting the pool.
+ /// </summary>
+ public int Timeout { get; set; }
+
+ public int RetryCount { get; set; }
+ public TimeSpan RetryTimeout { get; set; }
+
+ /// <summary>
+ /// Time to wait in milliseconds to reconnect to the pool when all nodes are down.
+ /// </summary>
+ public int DeadTimeout { get; set; }
+
+ /// <summary>
+ /// Raised when the pool's configuration changes.
+ /// </summary>
+ public event Action<ClusterConfig> ClusterConfigChanged;
+
+ /// <summary>
+ /// Starts listening for configuration data. This method blocks until the initial configuration is received. (Or until all pool urls fail.)
+ /// </summary>
+ public void Start()
+ {
+ var reset = this.mre = new ManualResetEvent(false);
+
+ // subscribe to the config url
+ this.listener = this.GetPooledListener();
+
+ // this will be signaled by the config changed event handler
+ reset.WaitOne();
+
+ // set to null, then dispose, so RaiseConfigChanged will not
+ // fail at Set when the config changes while we're cleaning up here
+ this.mre = null;
+ ((IDisposable)reset).Dispose();
+ }
+
+ public void Stop()
+ {
+ this.ReleaseListener(this.listener);
+ this.listener = null;
+ }
+
+ private static readonly JavaScriptConverter[] KnownConverters = { ClusterNode.ConverterInstance };
+
+ private void HandleMessage(string message)
+ {
+ // everything failed
+ if (String.IsNullOrEmpty(message))
+ {
+ this.lastHash = null;
+ this.RaiseConfigChanged(null);
+ return;
+ }
+
+ // deserialize the buckets
+ var jss = new JavaScriptSerializer();
+ jss.RegisterConverters(KnownConverters);
+
+ var config = jss.Deserialize<ClusterConfig>(message);
+
+ // check if the config is the same as the previous
+ // we cannot compare the messages because they have more information than we deserialize from them
+ var configHash = config.GetHashCode();
+
+ if (lastHash != configHash)
+ {
+ lastHash = configHash;
+ this.RaiseConfigChanged(config);
+ }
+ else if (log.IsDebugEnabled)
+ log.Debug("Last message was the same as current, ignoring.");
+ }
+
+ private void RaiseConfigChanged(ClusterConfig config)
+ {
+ var ccc = this.ClusterConfigChanged;
+
+ // we got a new config, notify the pool to reload itself
+ if (ccc != null)
+ ccc(config);
+
+ // trigger the event so Start stops blocking
+ if (this.mre != null)
+ this.mre.Set();
+ }
+
+ #region [ message listener pooling ]
+ private static readonly object ListenerSync = new Object();
+
+ // we pool and refcount the listeners here so we can safely dispose them when all clients are destroyed
+ private static Dictionary<int, MessageStreamListener> listeners = new Dictionary<int, MessageStreamListener>();
+ private static Dictionary<MessageStreamListener, ListenerInfo> listenerRefs = new Dictionary<MessageStreamListener, ListenerInfo>();
+
+ private class ListenerInfo
+ {
+ public int RefCount;
+ public int HashKey;
+ }
+
+ /// <summary>
+ /// Unsubscibes from a pooled listener, and destrpys it if no additionals subscribers are present.
+ /// </summary>
+ /// <param name="listener"></param>
+ private void ReleaseListener(MessageStreamListener listener)
+ {
+ lock (ListenerSync)
+ {
+ listener.Unsubscribe(this.HandleMessage);
+
+ var info = listenerRefs[listener];
+ if (info.RefCount == 1)
+ {
+ listenerRefs.Remove(listener);
+ listeners.Remove(info.HashKey);
+
+ try { using (listener) listener.Stop(); }
+ catch { }
+ }
+ else
+ {
+ info.RefCount--;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Returns a MessageStreamListener instance based on this instance's configuratino (timeout, bucket name etc.)
+ ///
+ /// When multiple listeners are requested with the exact same parameters (usually when multiple clients are instantiated from the same configuration),
+ /// the same listener will be returned each time.
+ /// </summary>
+ /// <returns></returns>
+ private MessageStreamListener GetPooledListener()
+ {
+ // create a unique key based on the parameters
+ // to find out if we already have a listener attached to this pool
+ var hcc = new HashCodeCombiner();
+
+ hcc.Add(this.Timeout);
+ hcc.Add(this.DeadTimeout);
+ hcc.Add(this.RetryCount);
+ hcc.Add(this.RetryTimeout.GetHashCode());
+ hcc.Add(this.bucketName.GetHashCode());
+
+ if (credential != null)
+ {
+ hcc.Add((this.credential.UserName ?? String.Empty).GetHashCode());
+ hcc.Add((this.credential.Password ?? String.Empty).GetHashCode());
+ hcc.Add((this.credential.Domain ?? String.Empty).GetHashCode());
+ }
+
+ for (var i = 0; i < this.poolUrls.Length; i++)
+ hcc.Add(this.poolUrls[i].GetHashCode());
+
+ var hash = hcc.CurrentHash;
+
+ MessageStreamListener retval;
+
+ lock (ListenerSync)
+ if (listeners.TryGetValue(hash, out retval))
+ {
+ listenerRefs[retval].RefCount++;
+ retval.Subscribe(this.HandleMessage);
+ }
+ else
+ {
+ var name = this.bucketName;
+
+ // create a new listener for the pool urls
+ retval = new MessageStreamListener(poolUrls, (client, root) => ResolveBucketUri(client, root, name));
+
+ retval.ConnectionTimeout = this.Timeout;
+ retval.DeadTimeout = this.DeadTimeout;
+ retval.Credentials = this.credential;
+ retval.RetryCount = this.RetryCount;
+ retval.RetryTimeout = this.RetryTimeout;
+
+ retval.Subscribe(this.HandleMessage);
+
+ listeners[hash] = retval;
+ listenerRefs[retval] = new ListenerInfo { RefCount = 1, HashKey = hash };
+
+ retval.Start();
+ }
+
+ return retval;
+ }
+
+ private static Uri ResolveBucketUri(WebClientWithTimeout client, Uri root, string bucketName)
+ {
+ try
+ {
+ var bucket = ConfigHelper.ResolveBucket(client, root, bucketName);
+ if (bucket == null)
+ return null;
+
+ if (String.IsNullOrEmpty(bucket.streamingUri))
+ {
+ log.ErrorFormat("Url {0} for bucket {1} returned a config with no streamingUri", root, bucketName);
+ return null;
+ }
+
+ return new Uri(root, bucket.streamingUri);
+ }
+ catch (Exception e)
+ {
+ log.Error("Error resolving streaming uri: " + root, e);
+
+ return null;
+ }
+ }
+
+ #endregion
+ }
+}
+
+#region [ License information ]
+/* ************************************************************
+ *
+ * Copyright (c) 2010 Attila Kiskó, enyim.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ************************************************************/
+#endregion
@@ -0,0 +1,7 @@
+# Couchbase Client Version History
+
+## Pre-Release
+
+ * Former Membase client refactored to reflect product namechange to Couchbase.
+ * CBS 2.0 support temporarily removed. This release of the client library will support CBS 1.8 only.
+
Oops, something went wrong.

0 comments on commit d53ddca

Please sign in to comment.