Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version

Practical .NET Remoting Caching Sink

, 27 Jun 2003
A cache mechanism for .NET Remoting
using System;
using System.Diagnostics;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Collections;
using System.Runtime.Remoting.Messaging;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;

namespace System.Runtime.Remoting.Caching
{
	public class CachingClientSinkProvider: IClientChannelSinkProvider 
	{
		private IClientChannelSinkProvider _next;

		public CachingClientSinkProvider(IDictionary properties, ICollection providerData) 
		{
		}

		public IClientChannelSinkProvider Next 
		{
			get {return _next; }
			set {_next = value;}
		}

		public IClientChannelSink CreateSink(IChannelSender channel, string url, object remoteChannelData) 
		{
			IClientChannelSink next = _next.CreateSink(channel, url, remoteChannelData);	
			return new CachingClientSink(next);
		}
	}
	[AttributeUsage(AttributeTargets.Method, 
		 Inherited = true, 
		 AllowMultiple = false)]
	public class InfinitelyCachedAttribute : CacheTimeAttribute
	{
		public InfinitelyCachedAttribute() : base(TimeSpan.FromDays(100)){}
	}
	[AttributeUsage(AttributeTargets.Method, 
		 Inherited = true, 
		 AllowMultiple = false)]
	public class CacheTimeAttribute : Attribute
	{
		private TimeSpan time;  
		public TimeSpan Time{    get { return time; }    
			set { this.time = value; }  
		}
		public CacheTimeAttribute(TimeSpan time)  {    this.time=time;  }
		public CacheTimeAttribute(string time)  {    this.time=TimeSpan.Parse(time);  }
	}
	public class CachingServerSink: BaseChannelSinkWithProperties,
		IServerChannelSink 
	{
		private IServerChannelSink _nextSink;

		public CachingServerSink(IServerChannelSink next) 
		{
			_nextSink = next;
		}

		public IServerChannelSink NextChannelSink 
		{
			get 
			{
				return _nextSink;
			}
		}

		public void AsyncProcessResponse(IServerResponseChannelSinkStack sinkStack, 
			object state, IMessage msg, ITransportHeaders headers, Stream stream) 
		{
			IMethodCallMessage mc = msg as IMethodCallMessage;
			if(mc!=null)
				headers["X-Cache"] = this.GetMethodCacheLimit(mc.MethodBase).ToString();

			sinkStack.AsyncProcessResponse(msg,headers,stream);
		}

		public Stream GetResponseStream(IServerResponseChannelSinkStack sinkStack, 
			object state, IMessage msg, ITransportHeaders headers) 
		{
			return null;
		}
		string GetMethodCacheLimit(MethodBase m)
		{
			foreach(CacheTimeAttribute a in m.GetCustomAttributes(typeof(CacheTimeAttribute),true))
			{
				return a.Time.ToString();
			}
			return null;
		}
		public ServerProcessing ProcessMessage(IServerChannelSinkStack sinkStack, IMessage requestMsg, 
			ITransportHeaders requestHeaders, Stream requestStream, out IMessage responseMsg, 
			out ITransportHeaders responseHeaders, out Stream responseStream) 
		{

			sinkStack.Push(this,null);

			ServerProcessing res = _nextSink.ProcessMessage(sinkStack, requestMsg, requestHeaders,
				requestStream, out responseMsg, out responseHeaders, out responseStream);
			IMethodCallMessage mc = requestMsg as IMethodCallMessage;
			if(mc!=null)
			{
				string t = this.GetMethodCacheLimit(mc.MethodBase);
				if(t!=null)
				{
					if(responseHeaders==null)responseHeaders=new TransportHeaders();
					responseHeaders["X-Cache"] = t;
				}
			}
			return res;
		}
	}
	public class CachingServerSinkProvider: IServerChannelSinkProvider 
	{
		private IServerChannelSinkProvider _nextProvider;

		public CachingServerSinkProvider(IDictionary properties, ICollection providerData) 
		{ 
		}

		public IServerChannelSinkProvider Next 
		{
			get {return _nextProvider; }
			set {_nextProvider = value;}
		}

		public IServerChannelSink CreateSink(IChannelReceiver channel) 
		{
			IServerChannelSink next = _nextProvider.CreateSink(channel);				
				
			return new CachingServerSink(next);
		}

		public void GetChannelData(IChannelDataStore channelData) 
		{
		}
	}
	public class CachingClientSink: BaseChannelSinkWithProperties, 
		IClientChannelSink 
	{
		private IClientChannelSink _nextSink;
		private static RemoteCallCache cache = new RemoteCallCache();

		public CachingClientSink(IClientChannelSink next) 
		{
			_nextSink = next;
		}

		public IClientChannelSink NextChannelSink 
		{
			get 
			{
				return _nextSink;
			}
		}

		public void AsyncProcessRequest(IClientChannelSinkStack sinkStack, 
			IMessage msg,  ITransportHeaders headers, Stream stream) 
		{
			_nextSink.AsyncProcessRequest(sinkStack,msg,headers,stream);
		}

		public void AsyncProcessResponse(IClientResponseChannelSinkStack sinkStack, 
			object state,  ITransportHeaders headers, Stream stream) 
		{
			sinkStack.AsyncProcessResponse(headers,stream);
		}

		public Stream GetRequestStream(IMessage msg, 
			ITransportHeaders headers) 
		{
			return _nextSink.GetRequestStream(msg, headers);
		}

		[Conditional("DEBUG")]
		void Trace(string msg)
		{
			System.Diagnostics.Trace.WriteLine(msg,"Caching");
		}
		public void ProcessMessage(IMessage msg, ITransportHeaders requestHeaders, 
			Stream requestStream, out ITransportHeaders responseHeaders, out Stream responseStream) 
		{
			RemotingKey k = new RemotingKey(msg, requestStream, requestHeaders);				
			RemotingResult result = cache.GetPreviousResults(k);
			if (result == null)
			{
				_nextSink.ProcessMessage(msg, requestHeaders, requestStream, out responseHeaders, out responseStream);
				string cacheLimit = responseHeaders["X-Cache"] as string;
				if (cacheLimit != null)
				{
					try
					{
						TimeSpan t = TimeSpan.Parse(cacheLimit);
						Trace("Cached " + ((IMethodMessage)msg).MethodName+" till "+t.ToString());

						RemotingResult r=new RemotingResult(responseStream, responseHeaders, DateTime.UtcNow + t);
						cache.CacheResults(k, r);
						responseStream=r.GetNewResponseStream();
					}
					catch{}
				}
			}
			else
			{
				Trace("Use cached result for " + ((IMethodMessage)msg).MethodName);
				responseStream = result.GetNewResponseStream();
				responseHeaders = result.Headers;
			}
		}
	}
	[Serializable]
	public class RemotingKey
	{
		public override int GetHashCode()
		{
			return hashCode;
		}
		public override bool Equals(object o)
		{
			RemotingKey ok = o as RemotingKey;
			if(ok==null)return false;

			if (this.key.Length != ok.key.Length)
				return false;

			for(int i = 0; i < key.Length; ++i)
			{
				if (key[i] != ok.key[i])
					return false;
			}
			return true;
		}
		public RemotingKey(){}
		byte[] _key=null;
		public void CalculateKey()
		{
			if(msg==null || _key!=null)return;
			BinaryFormatter bf = new BinaryFormatter();
			MemoryStream ms = new MemoryStream();
			bf.Serialize(ms, msg);
			bf.Serialize(ms, headers);
			_key = ms.GetBuffer();
			msg=null; headers=null;				
		}
		public byte[] key
		{
			get
			{
				if(_key!=null)return _key;
				try
				{
					CalculateKey();
					return _key;
				}
				catch
				{
					return new byte[0];
				}
			}
		}
		int hashCode;
		
		IMessage msg=null;
		ITransportHeaders headers=null;
		public RemotingKey(IMessage msg, Stream rs, ITransportHeaders headers)
		{
			IMethodMessage m = msg as IMethodMessage;
			if(m!=null)hashCode = m.MethodName.GetHashCode();
			else hashCode=0;
			
			if(rs!=null && rs.CanSeek && rs.CanRead)
			{
				MemoryStream t = new MemoryStream();
				if(m!=null)
				{
					StreamWriter s=new StreamWriter(t,Encoding.UTF8);
					s.WriteLine(m.Uri);
					s.WriteLine(m.TypeName);
					s.WriteLine(m.MethodName);
					s.Flush();
				}
				long op = rs.Position;
				RemotingResult.CopyStream(t,rs);
				rs.Seek(op,SeekOrigin.Begin);
				_key = t.GetBuffer();
			}
			else
			{
				this.msg=msg;
				this.headers=headers;
			}
		}
		/*public string StringReprensentation
		{
			get
			{
				StringBuilder sb=new StringBuilder();
				foreach(object e in _msg.Properties)
				{
					Array ee = e as Array;
					if(ee==null)
					{
						sb.Append(e.ToString());
					}
					else
					{
						foreach(object eee in ee)
						{
							sb.Append(eee);
						}
					}
				}
				foreach(object e in _headers)
				{
					Array ee = e as Array;
					if(ee==null)
					{
						sb.Append(e.ToString());
					}
					else
					{
						foreach(object eee in ee)
						{
							sb.Append(eee);
						}
					}
				}
				return sb.ToString();
				/*IMethodMessage m = _msg as IMethodMessage;
				if(m!=null)
				{
					sb.Append(m.TypeName);
					sb.Append(m.MethodName);
					sb.Append(m.ArgCount);
					foreach(object a in m.Args)
						sb.Append(a);
				}
			}
		}*/
	}

	[Serializable]
	public class RemotingResult
	{
		public static int CopyStream(Stream to, Stream from)
		{
			if(from.CanSeek)from.Seek(0,SeekOrigin.Begin);

			byte[] buffer = new byte[256];
			int size = 0;
			while(true) 
			{
				int sizeRead = from.Read(buffer,0,buffer.Length);
				if(sizeRead==0)break;
				to.Write(buffer,0,sizeRead);
				size += sizeRead;
			}
			to.Seek(0,SeekOrigin.Begin);
			return size;
		}		
		
		public RemotingResult(Stream Response, ITransportHeaders ResponseHeaders, DateTime ValidTo)
		{
			CopyStream(_response,Response);
			_headers = ResponseHeaders;
			_validTo = ValidTo;
		}

		public Stream GetNewResponseStream()
		{
			Stream r = new MemoryStream((int)_response.Length);
			CopyStream(r,_response);
			return r;
		}
		public ITransportHeaders Headers{get{return _headers;}}
		public DateTime ValidTo{get{return _validTo;}}

		MemoryStream _response=new MemoryStream();
		private ITransportHeaders _headers;
		private DateTime _validTo;
	}

	public class RemoteCallCache 
	{
		private System.Collections.Hashtable _resultMap = Hashtable.Synchronized(new Hashtable());
		
		public RemotingResult GetPreviousResults(RemotingKey k)
		{
			try
			{
				RemotingResult res = (RemotingResult)_resultMap[k];
				if (res != null && res.ValidTo < DateTime.UtcNow)
				{
					_resultMap.Remove(k);
					return null;
				}
				return res;
			}
			catch(Exception)
			{
				return null;
			}
		}
	
		public void CacheResults(RemotingKey k, RemotingResult Result)
		{
			k.CalculateKey();
			_resultMap[k] = Result;
		}
	}

}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

zhi
Researcher
United States United States
My name is Zhiheng Cao. I major in Eletronics Engineering, in University of Tokyo. I do part time jobs of computer programming and teaching high school students English and Mathematics.
 
CodeProject provided me with valuable information for my programming job many times in the past. I hope I can do my part by providing goodies I have.
 

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.150224.1 | Last Updated 28 Jun 2003
Article Copyright 2003 by zhi
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid