// $Id$
//
// Copyright 2007-2008 Cisco Systems Inc.
//
// 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.
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using Etch.Transport;
using Etch.Util;
namespace Etch.Support
{
///
/// Interface to transport factory
///
public abstract class TransportFactory
{
///
/// Constructs a new Transport stack topped by a DeliveryService
/// which is used by the remote service implementations to send
/// messages and make calls.
///
/// transport configuration parameters.
/// additional resources needed by the stack.
/// the DeliveryService topping the transport stack.
protected abstract DeliveryService NewTransport( string uri, Resources resources );
///
/// Constructs a new Transport stack topped by a DeliveryService
/// which is used by the remote service implementations to send
/// messages and make calls.
///
/// transport configuration parameters.
/// additional resources needed by the stack.
/// the DeliveryService topping the transport stack.
public static DeliveryService GetTransport( string uri,Resources resources )
{
URL u = new URL( uri );
TransportFactory f = GetTransportFactory( u.Scheme );
return f.NewTransport( uri, resources );
}
///
/// Constructs a new Transport Listener which is used to construct
/// server sessions.
///
/// listener configuration parameters.
/// additional resources needed by the listener.
/// constructs the actual service class instances.
/// an out-of-band source which may be used to control the listener.
protected abstract Transport NewListener(string uri, Resources resources, ServerFactory factory);
///
/// Constructs a new Transport Listener which is used to construct
/// server sessions.
///
/// listener configuration parameters.
/// additional resources needed by the listener.
/// constructs the actual service class instances.
/// an out-of-band source which may be used to control the listener.
public static Transport GetListener( string uri, Resources resources, ServerFactory factory )
{
URL u = new URL( uri );
TransportFactory f = GetTransportFactory( u.Scheme );
return f.NewListener( uri, resources, factory );
}
///
/// Adds any message filters specified on the uri. They are added in order
/// from transport to session. The first filter is the session for Messagizer,
/// the second is the session for the first, etc. The last filter added is
/// returned, and becomes the TransportMessage for what follows.
///
///
///
///
/// the most recently added filter
protected TransportMessage AddFilters(TransportMessage transport, URL uri, Resources resources)
{
string s = uri.GetTerm(FILTER);
if (s == null || s.Length == 0)
return transport;
StringTokenizer st = new StringTokenizer(s, ",:;| \t\r\n");
while (st.HasMoreTokens())
{
string t = st.NextToken();
transport = AddFilter(transport, t, uri, resources);
}
return transport;
}
///
/// Query term on the transport uri which defines a set of filters which
/// process messages as they move up and down the transport stack. Filter
/// names are separated by one or more of these characters: ",:;| \t\r\n".
///
/// Usage example: tcp://host:port?filter=KeepAlive,PwAuth
///
public const string FILTER = "filter";
private TransportMessage AddFilter(TransportMessage transport, string name,
URL uri, Resources resources)
{
string typeName = GetFilter(name);
if (typeName == null)
throw new ArgumentException(
string.Format( "Class '{0}' for filter name '{1}' not defined", typeName, name ) );
Type type = Type.GetType(typeName);
ConstructorInfo c = type.GetConstructor(FILTER_PARAMS);
return (TransportMessage) c.Invoke(new object[] { transport, uri, resources });
}
private readonly static Type[] FILTER_PARAMS = { typeof(TransportMessage), typeof(URL), typeof(Resources) };
///
/// Gets the fully qualified type name of the filter.
///
///
/// the fully qualified type name of the filter.
public static string GetFilter(string name)
{
lock (filters)
{
string value;
return filters.TryGetValue(name, out value) ? value : null;
}
}
///
/// Sets the fully qualified type name of the filter.
///
///
///
public static void DefineFilter(string name, string typeName)
{
lock (filters)
{
filters.Add(name, typeName);
}
}
private static readonly Dictionary filters =
new Dictionary();
///
/// Gets the named transport factory.
///
/// the name of a configured transport factory.
/// the named transport factory.
private static TransportFactory GetTransportFactory( string name )
{
object tfspec = Get(name);
if (tfspec == null)
throw new IOException("transport factory scheme '" + name + "' unknown");
if (tfspec is string)
tfspec = Type.GetType((string)tfspec);
if (tfspec is Type)
{
ConstructorInfo c = ((Type)tfspec).GetConstructor(FACTORY_PARAMS);
tfspec = c.Invoke(FACTORY_ARGS);
}
if (tfspec is TransportFactory)
return (TransportFactory)tfspec;
throw new ArgumentException(String.Format(
"Cannot create an instance of TransportFactory from transport factory name '{0}'", name));
}
private readonly static Type[] FACTORY_PARAMS = { };
private readonly static object[] FACTORY_ARGS = { };
public static object Get(string name)
{
lock (transportFactories)
{
object value;
return transportFactories.TryGetValue(name, out value) ? value : null;
}
}
///
/// Puts a named transport factory.
///
/// the uri scheme of this transport factory.
/// the transport factory spec to associate with the
/// name. This can be an instance of TransportFactory or the name of a subclass
/// of TransportFactory to instantiate.
public static void Define( string name, object tfspec )
{
lock (transportFactories)
{
transportFactories.Add(name, tfspec);
}
}
private readonly static Dictionary transportFactories =
new Dictionary();
static TransportFactory()
{
Define("tcp", new TcpTransportFactory(false));
Define("tls", new TcpTransportFactory(true));
DefineFilter("KeepAlive", "Etch.Transport.Filter.KeepAlive");
DefineFilter("PwAuth", "Etch.Transport.Filter.PwAuth");
DefineFilter("Logger", "Etch.Transport.Filter.Logger");
}
}
}