You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@etch.apache.org by sc...@apache.org on 2009/02/27 17:37:19 UTC

svn commit: r748580 [2/5] - in /incubator/etch/branches/router: ./ services/router/ services/router/scripts/ services/router/src/main/etch/ services/router/src/main/java/cisco/ services/router/src/main/java/org/ services/router/src/main/java/org/apache...

Added: incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/EtchRouterManager.java
URL: http://svn.apache.org/viewvc/incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/EtchRouterManager.java?rev=748580&view=auto
==============================================================================
--- incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/EtchRouterManager.java (added)
+++ incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/EtchRouterManager.java Fri Feb 27 16:37:17 2009
@@ -0,0 +1,854 @@
+/* $Id$
+ *
+ * Copyright 2009-2010 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.
+ */
+package org.apache.etch.services.router;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.etch.bindings.java.msg.Direction;
+import org.apache.etch.bindings.java.msg.Field;
+import org.apache.etch.bindings.java.msg.Message;
+import org.apache.etch.bindings.java.msg.StructValue;
+import org.apache.etch.bindings.java.msg.Type;
+import org.apache.etch.bindings.java.support.DeliveryService;
+import org.apache.etch.bindings.java.support.Mailbox;
+import org.apache.etch.bindings.java.support.ServerFactory;
+import org.apache.etch.services.router.ConnectionStackInfo.ConnectionType;
+import org.apache.etch.services.router.EtchRouter.EtchRouterException;
+import org.apache.etch.services.router.EtchRouter.PluginServiceState;
+import org.apache.etch.services.router.plugin.PluginGroup;
+import org.apache.etch.services.router.plugin.PluginMemberConnection;
+import org.apache.etch.services.router.plugin.PluginStateMonitor;
+import org.apache.etch.services.router.utils.LocalTypeImportExportHelper;
+import org.apache.etch.services.router.utils.StructValueImportExportHelper;
+import org.apache.etch.services.router.utils.XmlBindingData;
+import org.apache.etch.services.router.utils.XmlBindingDataParser;
+import org.apache.etch.util.core.Who;
+import org.apache.etch.util.core.io.Session;
+import org.apache.etch.util.core.io.Transport;
+
+/**
+ * Main class for the Etch Router service
+ * 
+ * @author Wei Wang (weiwa@cisco.com)
+ *
+ */
+public class EtchRouterManager
+{
+	/**
+	 * Key in the resources map for this EtchRouterManager
+	 */
+	public static final String _ETCH_ROUTER_MANAGER = "EtchRouterManager";
+	
+	private static final Logger _LOGGER = Logger.getLogger( EtchRouterManager.class.getName());
+	
+	private Properties _properties = null;
+	
+	private File _homeDir = null;
+	
+	private File _appsRoot = null;
+	
+	private File _pluginsRoot = null;
+	
+	private String _url = null;
+	
+	private final Object _syncObj = new Object();
+	
+	private DynamicValueFactory _valueFactory = null;
+	
+	private Map<String, PluginGroup> _pluginMap = null;
+	
+	private Map<Type, PluginGroup> _method2PluginMap = null;
+	
+	private Map<String, ApplicationInstallInfo> _appMap = null;
+	
+	// all methods defined by the EtchRouter's own XML binding file:
+	private Map<Type, Boolean> _localMethodsMap = null;
+	
+	private Map<EtchRouterMessageFilter, ConnectionStackInfo> _connectionInfoMapByFilter = null;
+	
+	private Map<DeliveryService, ConnectionStackInfo> _connInfoMapByDs = null;
+	
+	private ERStubHelper _stubHelper = new ERStubHelper();
+	
+	private Map<ConnectionStackInfo, ApplicationConnectionInfo> _appConnectionInfoMap = null;
+	
+	private Map<ConnectionStackInfo, ApplicationConnectionInfo> _pluginServerConn2AppConnMap = null;
+	
+	private static final String _REGISTER_ROUTER_APP_METHOD_NAME = "registerRouterApplication";
+	
+	private static final String _ROUTER_PLUGIN_SERVICE_STATE_CHANGE_METHOD_NAME = "routerPluginServiceStateChange";
+	
+	private Type _registerApplicationMethodType = null;
+	
+	private Field _mf_applicationName = new Field("applicationName");
+	private Field _mf_applicationParam = new Field("applicationParam");
+	
+	private Type _pluginServiceStateChangeMethodType = null;
+	private Field _mf_pluginGroupName = new Field("pluginGroupName");
+	private Field _mf_state = new Field("state");
+	private Field _mf_detail = new Field("detail");
+	
+	//number of miliseconds
+	private int _connStartMaxDelay = 4000;
+	
+	// number of miliseconds
+	private int _connStopMaxDelay = 4000;
+	
+	/**
+	 * Constructor
+	 * 
+	 * @param properties
+	 * @throws Exception
+	 */
+	public EtchRouterManager(Properties properties) throws Exception
+	{
+		_properties = properties;
+		initProperties();
+		
+		_pluginMap = Collections.synchronizedMap( new HashMap<String, PluginGroup>() );
+		_method2PluginMap = Collections.synchronizedMap( new HashMap<Type, PluginGroup>() );
+		_appMap = Collections.synchronizedMap( new HashMap<String, ApplicationInstallInfo>() );
+		_valueFactory = new DynamicValueFactory(_url);
+		_localMethodsMap = new HashMap<Type, Boolean>();
+		_connectionInfoMapByFilter = Collections.synchronizedMap( new HashMap<EtchRouterMessageFilter, ConnectionStackInfo>() );
+		_connInfoMapByDs = Collections.synchronizedMap( new HashMap<DeliveryService, ConnectionStackInfo>() );
+		_appConnectionInfoMap = Collections.synchronizedMap( new HashMap<ConnectionStackInfo, ApplicationConnectionInfo>() );
+		_pluginServerConn2AppConnMap = Collections.synchronizedMap( new HashMap<ConnectionStackInfo, ApplicationConnectionInfo>() );
+	}
+	
+	private void initProperties() throws Exception
+	{
+		_url = _properties.getProperty( "listener.url" );
+		_homeDir = new File(_properties.getProperty( "etch.router.home", "." ));
+		if (_url==null)
+			throw new Exception("The property 'listener.url' is not specified in properties");
+		if (!(_homeDir.exists() && _homeDir.isDirectory()))
+			throw new Exception(String.format( "Invalid home directory: %s", _homeDir.getPath() ));
+		String appsRoot = _properties.getProperty( "applications.root.dir" );
+		_appsRoot = appsRoot==null ? new File(_homeDir, "applications") : new File(appsRoot);
+		if ((!_appsRoot.exists()) || (!_appsRoot.isDirectory()))
+			throw new Exception(String.format( "The applications root directory does not exist: %s", _appsRoot.getAbsolutePath()));
+		String pluginsRoot = _properties.getProperty( "plugins.root.dir" );
+		_pluginsRoot = pluginsRoot==null ? new File(_homeDir, "plugins") : new File(pluginsRoot);
+		if ((!_pluginsRoot.exists()) || (!_pluginsRoot.isDirectory()))
+			throw new Exception(String.format( "The plugins root directory does not exist: %s", _pluginsRoot.getAbsolutePath()));
+	
+		try
+		{
+			_connStartMaxDelay = Integer.parseInt( _properties.getProperty( "connection.start.max.delay" ) );
+		}
+		catch (Exception e)
+		{
+			
+		}
+		try
+		{
+			_connStopMaxDelay = Integer.parseInt( _properties.getProperty( "connection.stop.max.delay" ) );
+		}
+		catch (Exception e)
+		{
+			
+		}
+	}
+	
+	public String getProperty(String key, String defaultValue)
+	{
+		return _properties.getProperty( key, defaultValue );
+	}
+	
+	/**
+	 * Max delay time when calling _startAndWaitUp
+	 * 
+	 * @return
+	 */
+	public int getConnectionStartMaxDelay()
+	{
+		return _connStartMaxDelay;
+	}
+	
+	/**
+	 * Max delay time when calling _stopAndWaitDown
+	 * 
+	 * @return
+	 */
+	public int getConnectionStopMaxDelay()
+	{
+		return _connStopMaxDelay;
+	}
+	
+	/**
+	 * Call back method
+	 * 
+	 * @param svc
+	 * @param sender
+	 * @param msg
+	 */
+	public void handleStubHelperCall(DeliveryService svc, Who sender, Message msg)
+	{
+		_LOGGER.log( Level.FINEST, "Method called, msg's type name is \"{0}\"", msg.type().getName());
+	}
+	
+	/**
+	 * 
+	 * @param event
+	 * @param connStackInfo
+	 * @throws Exception
+	 */
+	public void sessionNotify(Object event, ConnectionStackInfo connStackInfo) throws Exception
+	{
+		if (event != Session.DOWN) return;
+		_LOGGER.log( Level.INFO, "Method starts, event is: {0}, connection is: {1}",
+			new Object[] { event, connStackInfo } );
+    	ConnectionType connType = connStackInfo.getConnectionType();
+    	if (connType==null)
+    	{
+    		removeConnectionStackInfo( connStackInfo );
+    	}
+    	else if (connType==ConnectionType.APP_CLIENT_CONN)
+    	{
+   			ApplicationConnectionInfo appConnInfo = this.getAppConnectionInfoByAppClientStackInfo( connStackInfo );
+   			if (appConnInfo!=null)
+   			{
+   				appConnInfo.stopAllPluginConnections();
+   				_appConnectionInfoMap.remove( connStackInfo );
+   	   			Collection<PluginGroup> pluginGrps = appConnInfo.getReferencedPluginGroups();
+       			for (PluginGroup pluginGrp : pluginGrps)
+       			{
+       				pluginGrp.removeApplicationConnection( appConnInfo );
+       			}
+   			}
+   			removeConnectionStackInfo( connStackInfo );
+   		}
+    	else if (connType==ConnectionType.PLUGIN_MONITOR_CONN)
+    	{
+   			PluginGroup pluginGroup = this.getPluginGroup( connStackInfo.getPluginOrAppName() );
+   			pluginGroup.onPluginMemberConnectionDown( connStackInfo );
+   			removeConnectionStackInfo( connStackInfo );
+     	}
+    	else if (connType==ConnectionType.PLUGIN_SERVER_CONN)
+    	{
+   			ApplicationConnectionInfo appConnInfo = this.getAppConnectionInfoByPluginServerStackInfo( connStackInfo );
+   			if (appConnInfo!=null)
+   			{
+   				PluginGroup pluginGrp = appConnInfo.getPluginGroupByName( connStackInfo.getPluginOrAppName() );
+   				PluginMemberConnection instConn = appConnInfo.getPluginConnectionByPluginGroup( pluginGrp );
+   				//remove the corresponding plugin instance from available list before re-init:
+   				pluginGrp.onPluginMemberConnectionDown( instConn.getMember().getConnInfo() );
+   				appConnInfo.stopPluginConnection( pluginGrp, true );
+   				appConnInfo.initPluginConnection( pluginGrp );
+   			}
+   			removeConnectionStackInfo( connStackInfo );
+     	}
+	}
+	
+	
+	/**
+	 * This is called by ERMessagizer.sessionPacket
+	 * 
+	 * @param sender
+	 * @param connStackInfo
+	 * @param msg
+	 */
+	public void handleLocalMethodMessage( Who sender, ConnectionStackInfo connStackInfo, Message msg)
+	{
+		ConnectionType connType = connStackInfo.getConnectionType();
+		Type methodType = msg.type();
+		Message rmsg = (methodType.getResult()==null) ? null : msg.reply();
+		Object result = null;
+		try
+		{
+			if (_registerApplicationMethodType.equals( methodType ))
+			{
+				if (connType==null)
+				{
+					result = registerApplication( sender, connStackInfo, msg );
+				}
+				else
+					throw new EtchRouterException(2, String.format( "Cannot register application because the connection was already established as type %s", connType.name() ));
+			}
+			else 
+			{
+				result = handleOtherLocalMessage( sender, connStackInfo, msg );
+			}
+		}
+		catch (Throwable e)
+		{
+			if (e instanceof EtchRouterException) result = e;
+			else if ( e instanceof RuntimeException ) result = e;
+			else result = new RuntimeException(e);
+		}
+		if (rmsg!=null)
+		{
+			if (result!=null) rmsg.put( DynamicValueFactory._mf_result, result);
+			try
+			{
+				connStackInfo.getDeliveryService().transportMessage( sender, rmsg );
+			}
+			catch (Exception ee)
+			{
+				_LOGGER.log( Level.SEVERE, "Got Exception: ", ee);
+			}
+		}
+		if (result instanceof Throwable)
+			_LOGGER.log( Level.SEVERE, "Got Exception: ", (Throwable)result);
+			
+	}
+	
+	private Object handleOtherLocalMessage( Who sender, ConnectionStackInfo connStackInfo, Message msg ) throws Exception
+	{
+		throw new EtchRouterException(1, String.format( "Etch-router API is not implemented: %s", msg.type().getName() ));
+	}
+	
+	private String registerApplication( Who sender, ConnectionStackInfo connStackInfo, Message msg ) throws Exception
+	{
+		String applicationName = (String)msg.get( _mf_applicationName );
+		String applicationParam = (String)msg.get( _mf_applicationParam );
+		return registerApplication(applicationName, applicationParam, connStackInfo);
+	}
+	
+	/**
+	 * 
+	 * @param connStackInfo
+	 * @param pluginMethod
+	 * @throws Exception
+	 */
+	public void registerAnonymousApplication( ConnectionStackInfo connStackInfo, Type pluginMethod ) throws Exception
+	{
+		synchronized (_syncObj)
+		{
+			ApplicationConnectionInfo connInfo = _appConnectionInfoMap.get( connStackInfo );
+			if (connInfo==null)
+			{
+				connStackInfo.setConnectionType( ConnectionType.APP_CLIENT_CONN, null );
+				connInfo = new ApplicationConnectionInfo( connStackInfo, this, pluginMethod );
+				_appConnectionInfoMap.put( connStackInfo, connInfo );
+			}
+		}
+	}
+	
+	/**
+	 * 
+	 * @param applicationName
+	 * @param appConnInfo
+	 * @throws Exception
+	 */
+	private String registerApplication( String applicationName, String applicationParam, ConnectionStackInfo appConnInfo )
+		throws Exception
+	{
+		//allow lazy loading of the application install directory
+		ApplicationInstallInfo appInstallInfo = null;
+		try
+		{
+			loadApplicationInstallInfo(applicationName);
+		}
+		catch (Exception ee)
+		{
+			//_LOGGER.log( Level.FINE, "Registering anonymous app connection as application \"{0}\": {1}", new Object[]{ applicationName, appConnInfo} );
+		}
+		synchronized (_syncObj)
+		{
+			ApplicationConnectionInfo connInfo = _appConnectionInfoMap.get( appConnInfo );
+			if (connInfo==null)
+			{
+				appConnInfo.setConnectionType( ConnectionType.APP_CLIENT_CONN, appInstallInfo==null ? null : applicationName );
+				connInfo = new ApplicationConnectionInfo( applicationParam, appInstallInfo, appConnInfo, this);
+				_appConnectionInfoMap.put( appConnInfo, connInfo );
+			}
+			else
+			{
+				if (connInfo.isAnonymous())
+				{
+					_LOGGER.log( Level.FINE, "Registering anonymous app connection as application \"{0}\": {1}", new Object[]{ applicationName, appConnInfo} );
+					connInfo.setApplicationName( applicationName );
+					connInfo.setApplicationParam( applicationParam );
+				}
+				else
+				{
+					throw new EtchRouterException(1, "The application client is already registered with Etch Router manager");
+				}
+			}
+		}
+		return "OK";
+	}
+	
+	/**
+	 * 
+	 * @param pluginGroupName
+	 * @param state
+	 * @param appClientConn
+	 * @throws Exception
+	 */
+	public void callAppClientMethod_pluginServiceStateChange( String pluginGroupName, PluginServiceState state, String detail, ConnectionStackInfo appClientConn ) throws Exception
+	{
+		Message msg = new Message(_pluginServiceStateChangeMethodType, _valueFactory);
+		msg.put( _mf_pluginGroupName, pluginGroupName );
+		msg.put( _mf_state, state );
+		msg.put( _mf_detail, detail );
+		callClientMethod( msg, appClientConn );
+	}
+	
+	/**
+	 * 
+	 * @param msgDetail
+	 * @param connStack
+	 * @throws Exception
+	 */
+	public void sentNotifyMsgToConnection( String msgDetail, ConnectionStackInfo connStack ) throws Exception
+	{
+		Message msg = new Message(_valueFactory.get_mt__exception(), _valueFactory);
+		msg.put( _valueFactory._mf_result, new RuntimeException(msgDetail) );
+		callClientMethod( msg, connStack );
+	}
+	
+	private Object callClientMethod( Message msg, ConnectionStackInfo connInfo ) throws Exception
+	{
+		Type methodType = msg.type();
+		if (methodType.getDirection()==Direction.SERVER)
+		{
+			throw new EtchRouterException(1, String.format( "Cannot send method '%s' to an EtchRouter client.", methodType.getName() ));
+		}
+		DeliveryService ds = connInfo.getDeliveryService();
+		Type resultType = methodType.getResult();
+		if (resultType==null)
+		{
+			//is oneWay:
+			try
+			{
+				ds.transportMessage( null, msg );
+				return null;
+			}
+			catch (Exception e)
+			{
+				throw new RuntimeException(String.format( "Unknown error while calling oneway client method '%s'", methodType.getName() ), e);
+			}
+		}
+		try
+		{
+			Mailbox mbox = ds.begincall( msg );
+			return ds.endcall( mbox, resultType );
+		}
+		catch (Exception e)
+		{
+			throw new RuntimeException(String.format( "Unknown error while calling twoway client method '%s'", methodType.getName() ), e);
+		}
+	}
+	
+	
+	/**
+	 *
+	 * @param info
+	 */
+	public void addConnectionStackInfo(ConnectionStackInfo info)
+	{
+		_connectionInfoMapByFilter.put( info.getMessageFilter(), info );
+		_connInfoMapByDs.put( info.getDeliveryService(), info );
+	}
+	
+	public ConnectionStackInfo getConnectionStackInfo( EtchRouterMessageFilter filter)
+	{
+		return _connectionInfoMapByFilter.get( filter );
+	}
+	
+	public ConnectionStackInfo getConnectionStackInfo( DeliveryService ds)
+	{
+		return _connInfoMapByDs.get( ds );
+	}
+	
+	public void removeConnectionStackInfo( ConnectionStackInfo info )
+	{
+		_connectionInfoMapByFilter.remove( info.getMessageFilter() );
+		_connInfoMapByDs.remove( info.getDeliveryService() );
+	}
+	
+	public ApplicationInstallInfo getApplicationInstallInfo(String appName)
+	{
+		return _appMap.get( appName );
+	}
+	
+	public Collection<PluginGroup> getPluginGroups()
+	{
+		return _pluginMap.values();
+	}
+	
+	public PluginGroup getPluginGroup(String pluginGroupName)
+	{
+		return _pluginMap.get( pluginGroupName );
+	}
+	
+	public PluginGroup getPluginGroup( Type method)
+	{
+		return _method2PluginMap.get(method);
+	}
+	
+	public ApplicationConnectionInfo getAppConnectionInfoByAppClientStackInfo( ConnectionStackInfo appStackInfo )
+	{
+		return _appConnectionInfoMap.get( appStackInfo );
+	}
+	
+	public ApplicationConnectionInfo getAppConnectionInfoByPluginServerStackInfo( ConnectionStackInfo pluginServerStackInfo )
+	{
+		return _pluginServerConn2AppConnMap.get( pluginServerStackInfo );
+	}
+	
+	public void addAppConnectionInfo( ConnectionStackInfo pluginServerStackInfo, ApplicationConnectionInfo appConnInfo )
+	{
+		_pluginServerConn2AppConnMap.put( pluginServerStackInfo, appConnInfo );
+	}
+	
+	public void removeAppConnectionInfo( ConnectionStackInfo pluginServerStackInfo )
+	{
+		_pluginServerConn2AppConnMap.remove( pluginServerStackInfo );
+	}
+	
+	public DynamicValueFactory getValueFactory()
+	{
+		return _valueFactory;
+	}
+	
+	public boolean isLocalMethodType( Type aType )
+	{
+		return _localMethodsMap.get( aType )!=null;
+	}
+	
+	private ServerFactory _listener = null;
+	
+	private PluginStateMonitor _pluginMon = null;
+	
+	/**
+	 * 
+	 * @throws Exception
+	 */
+	public void start() throws Exception
+	{
+		_LOGGER.log( Level.INFO, "Etch Router Manager starting up..." );
+		loadEtchRouterXmlBinding();
+		loadInstalledPlugins();
+		loadInstalledApplications();
+		_listener = EtchRouterManagerHelper.newListener( this, _url, null );
+		_listener.transportControl( Transport.START_AND_WAIT_UP, 4000 );
+		_pluginMon = new PluginStateMonitor( this );
+		Thread monThread = new Thread( _pluginMon );
+		_LOGGER.log( Level.INFO, "Starting plugin state monitor thread..." );
+		monThread.start();
+		_LOGGER.log( Level.INFO, "Etch Router Manager running..." );
+	}
+
+	/**
+	 * 
+	 * @throws Exception
+	 */
+	public void stop() throws Exception
+	{
+		_LOGGER.log( Level.INFO, "Stopping Etch Router Manager..." );
+		_pluginMon.stop();
+		_listener.transportControl( Transport.STOP_AND_WAIT_DOWN, 4000 );
+	}
+	
+	private void loadEtchRouterXmlBinding() throws Exception
+	{
+		File xmlFile = new File(_homeDir, "EtchRouter.xml");
+		_LOGGER.log( Level.FINE, "Loading EtchRouter XML binding file: {0}", xmlFile.getAbsolutePath() );
+		List<Type> loadedEnums = new ArrayList<Type>();
+		List<Type> loadedStructs = new ArrayList<Type>();
+		List<Type> loadedExceptions = new ArrayList<Type>();
+		List<Type> loadedMethods = new ArrayList<Type>();
+		XmlBindingData data = XmlBindingData.loadXmlBindingFile( xmlFile );
+		if (data.getExterns().size()>0)
+			throw new Exception("No \"extern\" statement is allowed in EctchRouter IDL!");
+		XmlBindingDataParser parser = new XmlBindingDataParser( data );
+		Collection<Type> myTypes = parser.getEnumTypes();
+		initImpExpHelpers( myTypes, parser );
+		loadedEnums.addAll( _valueFactory.addTypeDefinitions( myTypes ) );
+		myTypes = parser.getExceptionTypes();
+		initImpExpHelpers( myTypes, parser );
+		loadedExceptions.addAll( _valueFactory.addTypeDefinitions( myTypes ) );
+		myTypes = parser.getStructTypes();
+		initImpExpHelpers( myTypes, parser );
+		loadedStructs.addAll( _valueFactory.addTypeDefinitions( myTypes ) );
+		myTypes = parser.getMethodTypes();
+		loadedMethods.addAll( _valueFactory.addTypeDefinitions( myTypes )  );
+		//only primitive types are supported in the EtchRouter APIs:
+		if (loadedMethods.isEmpty())
+		{
+			_LOGGER.log( Level.WARNING, "No method is loaded to the dynamic value factory from the EtchRouter XML binding file: {0}", xmlFile.getAbsolutePath() );
+		}
+		for (Type methodType : loadedMethods)
+		{	
+			methodType.setStubHelper( _stubHelper );
+			_localMethodsMap.put( methodType, true );
+			if (_REGISTER_ROUTER_APP_METHOD_NAME.equals( methodType.getName() ) || methodType.getName().endsWith( "."+_REGISTER_ROUTER_APP_METHOD_NAME ))
+				_registerApplicationMethodType = methodType;
+			else if (_ROUTER_PLUGIN_SERVICE_STATE_CHANGE_METHOD_NAME.equals( methodType.getName() ) || methodType.getName().endsWith( "."+ _ROUTER_PLUGIN_SERVICE_STATE_CHANGE_METHOD_NAME ))
+				_pluginServiceStateChangeMethodType = methodType;
+		}
+		if (_registerApplicationMethodType==null)
+			throw new Exception(String.format( "Cannot find \"%s\" method definition in EtchRouter IDL", _REGISTER_ROUTER_APP_METHOD_NAME) );
+		if (_pluginServiceStateChangeMethodType==null)
+			throw new Exception( String.format( "Cannot find \"%s\" method definition in EtchRouter IDL", _ROUTER_PLUGIN_SERVICE_STATE_CHANGE_METHOD_NAME ) );
+	}
+
+	private void initImpExpHelpers(Collection<Type> types, XmlBindingDataParser parser)
+	{
+		for (Type myType : types)
+		{
+			LocalTypeImportExportHelper helper = parser.getLocalImportExportHelper( myType.getName() );
+			if (helper != null)
+			{
+				_valueFactory.addClass2TypeMap( helper.getTypeClass(), myType );
+				myType.setComponentType( helper.getTypeClass() );
+				myType.setImportExportHelper( helper );
+			}
+			else
+			{
+				StructValueImportExportHelper structHelper = parser.getStructValueImportExportHelper( myType.getName() );
+				if (structHelper!=null) 
+				{
+					myType.setComponentType( StructValue.class );
+					myType.setImportExportHelper( structHelper );
+				}
+			}
+		}
+	}
+	
+	/**
+	 * 
+	 * @throws Exception
+	 */
+	public void loadInstalledPlugins() throws Exception
+	{
+		String preloadPlugins = _properties.getProperty( "preload.plugins", "true" );
+		if (!"true".equalsIgnoreCase( preloadPlugins ))
+		{
+			_LOGGER.log( Level.FINE, "Plugin install info will be lazily loaded with plugin registration" );
+			return;
+		}
+		_LOGGER.log( Level.FINE, "Pre-loading all Plugins' install info from directory \"{0}\"...", _pluginsRoot.getAbsolutePath() );
+		File[] subFiles = _pluginsRoot.listFiles();
+		for (File subFile : subFiles)
+		{
+			if (subFile.isDirectory())
+				loadPluginInstallInfo(null, subFile);
+				
+		}
+	}
+	
+	/**
+	 * 
+	 * @throws Exception
+	 */
+	public void loadInstalledApplications() throws Exception
+	{
+		String preloadApps = _properties.getProperty( "preload.applications", "true" );
+		if (!"true".equalsIgnoreCase( preloadApps ))
+		{
+			_LOGGER.log( Level.FINE, "Application install info will be lazily loaded with application registration" );
+			return;
+		}
+		_LOGGER.log( Level.FINE, "Pre-loading all Applications' install info from directory \"{0}\"...", _appsRoot.getAbsolutePath() );
+		File[] subFiles = _appsRoot.listFiles();
+		for (File subFile : subFiles)
+		{
+			if (subFile.isDirectory())
+				loadApplicationInstallInfo(null, subFile);
+				
+		}
+	}
+	
+	/**
+	 * 
+	 * @param pluginName
+	 * @return
+	 * @throws Exception
+	 */
+	public PluginGroup loadPluginInstallInfo(String pluginName) throws Exception
+	{
+		PluginGroup pluginGrp = getPluginGroup(pluginName);
+		if (pluginGrp!=null)
+		{
+			_LOGGER.log( Level.FINER, "The plugin group info for \"{0}\" is already loaded to Etch Router manager", pluginName );
+			return pluginGrp;
+		}
+		File pluginDir = new File(_pluginsRoot, pluginName);
+		if ((!pluginDir.exists()) || (!pluginDir.isDirectory()))
+			throw new Exception(String.format( "The plugin directory does not exist: %s", pluginDir.getAbsolutePath()));
+		loadPluginInstallInfo(pluginName, pluginDir);
+		return getPluginGroup(pluginName);
+	}
+	
+	private void loadPluginInstallInfo(String pluginName, File pluginDir) throws Exception
+	{
+		if (pluginName==null) pluginName = pluginDir.getName();
+		_LOGGER.log( Level.FINE, "Loading Plugin install info \"{0}\" from directory \"{1}\" ", new Object[]{ pluginName, pluginDir.getAbsolutePath() }  );
+		File[] subFiles = pluginDir.listFiles();
+		List<File> xmlFiles = new ArrayList<File>(subFiles.length);
+		File metaFile = null;
+		for (File subFile : subFiles)
+		{
+			String lowName = subFile.getName().toLowerCase();
+			if (lowName.endsWith( ".xml" ))
+				xmlFiles.add( subFile );
+			else if (lowName.equals( "metadata.txt" ))
+				metaFile = subFile;
+		}
+		if (xmlFiles.size()==0)
+			throw new Exception(String.format( "The plugin directory does not contain any xml binding file: %s", pluginDir.getAbsolutePath()));
+		Properties metaProp = readMetaDataFile(metaFile);
+		PluginInstallInfo info = new PluginInstallInfo(pluginName, metaProp);
+		PluginGroup pluginGrp = PluginGroup.newPluginGroup( pluginName, this, info );
+		_LOGGER.log( Level.FINE, "Plugin group name: \"{0}\", type: \"{1}\"", new Object[]{ pluginName, pluginGrp.getClass().getName() } );
+		loadPluginXmlFiles(xmlFiles, pluginGrp);
+		if (_pluginMap.get( pluginName )==null)
+			_pluginMap.put( pluginName, pluginGrp );
+	}
+	
+	private Properties readMetaDataFile(File metaDataFile) throws Exception
+	{
+		Properties prop = new Properties();
+		if (metaDataFile==null) return prop;
+		prop.load( new FileInputStream(metaDataFile) );
+		return prop;
+	}
+	
+	private void loadPluginXmlFiles(List<File> xmlFiles, PluginGroup pluginGroup) throws Exception
+	{
+		List<Type> allEnums = new ArrayList<Type>();
+		List<Type> allStructs = new ArrayList<Type>();
+		List<Type> allExceptions = new ArrayList<Type>();
+		List<Type> allMethods = new ArrayList<Type>();
+		List<Type> allExterns = new ArrayList<Type>();
+		XmlBindingDataParser parser = null;
+		for (File xmlBindingFile : xmlFiles)
+		{
+			XmlBindingData data = XmlBindingData.loadXmlBindingFile( xmlBindingFile );
+			parser = new XmlBindingDataParser( data );
+			Collection<Type> myTypes = parser.getEnumTypes();
+			allEnums.addAll( _valueFactory.addTypeDefinitions( myTypes ) );
+			myTypes = parser.getExceptionTypes();
+			allExceptions.addAll( _valueFactory.addTypeDefinitions( myTypes ) );
+			myTypes = parser.getStructTypes();
+			allStructs.addAll( _valueFactory.addTypeDefinitions( myTypes ) );
+			myTypes = parser.getExternTypes();
+			allExterns.addAll(  _valueFactory.addTypeDefinitions( myTypes ) );
+			myTypes = parser.getMethodTypes();
+			allMethods.addAll( _valueFactory.addTypeDefinitions( myTypes )  );
+		}
+		initImpExpHelpers( allEnums, parser );
+		initImpExpHelpers( allExceptions, parser );
+		initImpExpHelpers( allStructs, parser );
+		initImpExpHelpers( allExterns, parser );
+		if (allMethods.isEmpty())
+		{
+			_LOGGER.log( Level.WARNING, "No method is loaded to the dynamic value factory from the plugin directory: {0}", xmlFiles.get( 0 ).getCanonicalFile().getParent() );
+		}
+		for (Type methodType : allMethods)
+		{
+			pluginGroup.getInstallInfo().addMethod( methodType );
+			methodType.setStubHelper( _stubHelper );
+			if (methodType.getDirection()!=Direction.CLIENT)
+				_method2PluginMap.put( methodType, pluginGroup );
+		}
+	}
+	
+	/**
+	 * 
+	 * @param applicationName
+	 * @return
+	 * @throws Exception
+	 */
+	public ApplicationInstallInfo loadApplicationInstallInfo(String applicationName) throws Exception
+	{
+		ApplicationInstallInfo appInfo = getApplicationInstallInfo( applicationName );
+		if (appInfo!=null)
+		{
+			_LOGGER.log( Level.FINER, "The application install info for \"{0}\" is already loaded to Etch Router manager", applicationName );
+			return appInfo;
+		}
+		File appDir = new File(_appsRoot, applicationName);
+		if ((!appDir.exists()) || (!appDir.isDirectory()))
+			throw new Exception(String.format( "The application directory does not exist: %s", appDir.getAbsolutePath()));
+		loadApplicationInstallInfo(applicationName, appDir);
+		return getApplicationInstallInfo( applicationName );
+	}
+	
+	private void loadApplicationInstallInfo(String applicationName, File appDir) throws Exception
+	{
+		if (applicationName==null) applicationName = appDir.getName();
+		_LOGGER.log( Level.FINE, "Loading Application install info \"{0}\" from directory \"{1}\"", new Object[]{ applicationName, appDir.getAbsolutePath() } );
+		File[] subFiles = appDir.listFiles();
+		List<File> xmlFiles = new ArrayList<File>(subFiles.length);
+		for (File subFile : subFiles)
+		{
+			if (subFile.getName().toLowerCase().endsWith( ".xml" ))
+				xmlFiles.add( subFile );
+		}
+		if (xmlFiles.size()==0)
+			throw new Exception(String.format( "The application directory does not contain any xml binding file: %s", appDir.getAbsolutePath()));
+		ApplicationInstallInfo info = new ApplicationInstallInfo(applicationName);
+		loadApplicationXmlFiles(xmlFiles, info);
+		if (_appMap.get( applicationName )==null)
+			_appMap.put( applicationName, info );
+	}
+	
+	private void loadApplicationXmlFiles(List<File> xmlFiles, ApplicationInstallInfo appInfo) throws Exception
+	{
+		String appName = appInfo.getName();
+		List<Type> allMethods = new ArrayList<Type>();
+		XmlBindingDataParser parser = null;
+		for (File xmlBindingFile : xmlFiles)
+		{
+			XmlBindingData data = XmlBindingData.loadXmlBindingFile( xmlBindingFile );
+			parser = new XmlBindingDataParser( data);
+			checkApplicationTypeDefinitions(parser.getEnumTypes(), xmlBindingFile, appName, "Enum");
+			checkApplicationTypeDefinitions(parser.getExceptionTypes(), xmlBindingFile, appName, "Exception");
+			checkApplicationTypeDefinitions(parser.getStructTypes(), xmlBindingFile, appName, "Struct");
+			List<Type> methods = parser.getMethodTypes();
+			allMethods.addAll(methods);
+			checkApplicationTypeDefinitions(methods, xmlBindingFile, appName, "Method");
+		}
+		if (allMethods.isEmpty())
+		{
+			_LOGGER.log( Level.WARNING, "No method is found in the application directory: {0}", xmlFiles.get( 0 ).getCanonicalFile().getParent() );
+		}
+		for (Type methodType : allMethods)
+		{
+			appInfo.addMethod( methodType );
+		}			
+	}
+	
+	private void checkApplicationTypeDefinitions(Collection<Type> types, File xmlFile, String appName, String typeType)
+	{
+		for (Type aType : types)
+		{
+			Type typeFound = _valueFactory.getType( aType.getId() );
+			if (typeFound==null)
+			{
+				_LOGGER.log( Level.WARNING, "The \"{0}\" type \"{1}\" in file \"{2}\" for application \"{3}\" is not defined in DynamicValueFactory.", 
+					new Object[]{ typeType, aType.getName(), xmlFile.getAbsolutePath(), appName } );
+			}
+		}
+	}
+}

Propchange: incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/EtchRouterManager.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/EtchRouterManager.java
------------------------------------------------------------------------------
    svn:keywords = "Author Date Id Revision"

Added: incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/EtchRouterManagerHelper.java
URL: http://svn.apache.org/viewvc/incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/EtchRouterManagerHelper.java?rev=748580&view=auto
==============================================================================
--- incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/EtchRouterManagerHelper.java (added)
+++ incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/EtchRouterManagerHelper.java Fri Feb 27 16:37:17 2009
@@ -0,0 +1,116 @@
+/* $Id$
+ *
+ * Copyright 2009-2010 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.
+ */
+package org.apache.etch.services.router;
+
+import org.apache.etch.bindings.java.msg.ValueFactory;
+import org.apache.etch.bindings.java.support.DefaultServerFactory;
+import org.apache.etch.bindings.java.support.DeliveryService;
+import org.apache.etch.bindings.java.support.Pool;
+import org.apache.etch.bindings.java.support.ServerFactory;
+import org.apache.etch.bindings.java.support.TransportHelper;
+import org.apache.etch.bindings.java.transport.DefaultDeliveryService;
+import org.apache.etch.bindings.java.transport.MailboxManager;
+import org.apache.etch.bindings.java.transport.PlainMailboxManager;
+import org.apache.etch.bindings.java.transport.TransportMessage;
+import org.apache.etch.services.router.ConnectionStackInfo.ConnectionType;
+import org.apache.etch.util.Resources;
+import org.apache.etch.util.core.io.Transport;
+
+/**
+ * 
+ * @author Wei Wang (weiwa@cisco.com)
+ *
+ */
+public class EtchRouterManagerHelper extends TransportHelper
+{
+
+	/**
+	 * 
+	 * @param routerMgr
+	 * @param uri
+	 * @param resources
+	 * @param implFactory
+	 * @return
+	 * @throws Exception
+	 */
+	public static ServerFactory newListener( final EtchRouterManager routerMgr, final String uri,
+		Resources resources )
+		throws Exception
+	{
+		final Resources res = initResources( resources );
+		res.put( EtchRouterManager._ETCH_ROUTER_MANAGER, routerMgr );
+
+		final Transport<ServerFactory> listener = EtchRouterMgrTransportFactory.getListener( uri, res );
+		
+		return new DefaultServerFactory( listener, routerMgr )
+		{
+				public void newServer( String uri, Resources resources,
+						TransportMessage transport ) throws Exception
+				{
+					ValueFactory vf = (ValueFactory) resources.get( Transport.VALUE_FACTORY );
+					MailboxManager x = new PlainMailboxManager( transport, uri, res );
+					DeliveryService d = new DefaultDeliveryService( x, uri, res );
+					ConnectionStackInfo info = routerMgr.getConnectionStackInfo( d );
+					ERRemoteClient client = new ERRemoteClient( d, vf, routerMgr, info );
+					ImplServer server = new ImplServer( client, routerMgr );
+					Pool qp = (Pool) res.get( QUEUED_POOL );
+					Pool fp = (Pool) res.get( FREE_POOL );
+					new ERStub( d, server, qp, fp );
+					client._start();
+				}
+	
+				public ValueFactory newValueFactory()
+				{
+					return new DynamicValueFactory( uri );
+				}
+				
+				@Override
+				public String toString()
+				{
+					return "PerfHelper.ServerFactory/" + listener;
+				}
+		};
+	}
+	
+	/**
+	 * Create a connection to an Etch plug-in server listener
+	 *  
+	 * @param etchRouterManager
+	 * @param uri
+	 * @param resources
+	 * @return
+	 * @throws Exception
+	 */
+	public static ERRemoteServer newServer( EtchRouterManager etchRouterManager, String uri,
+		Resources resources, String pluginName) throws Exception
+	{
+		final Resources res = initResources( resources );
+		final DynamicValueFactory vf = etchRouterManager.getValueFactory();
+		res.put( Transport.VALUE_FACTORY, vf );
+		res.put( EtchRouterManager._ETCH_ROUTER_MANAGER, etchRouterManager );
+
+		DeliveryService d = EtchRouterMgrTransportFactory.getTransport( uri, res );
+		ConnectionStackInfo info = etchRouterManager.getConnectionStackInfo( d );
+		info.setConnectionType( ConnectionType.PLUGIN_SERVER_CONN, pluginName );
+		ERRemoteServer server = new ERRemoteServer( d, vf, etchRouterManager, info );
+		ImplClient client = new ImplClient( server, etchRouterManager );
+		Pool qp = (Pool) res.get( QUEUED_POOL );
+		Pool fp = (Pool) res.get( FREE_POOL );
+		new ERStub( d, client, qp, fp );
+		return server;
+	}
+}

Propchange: incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/EtchRouterManagerHelper.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/EtchRouterManagerHelper.java
------------------------------------------------------------------------------
    svn:keywords = "Author Date Id Revision"

Added: incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/EtchRouterMessageFilter.java
URL: http://svn.apache.org/viewvc/incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/EtchRouterMessageFilter.java?rev=748580&view=auto
==============================================================================
--- incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/EtchRouterMessageFilter.java (added)
+++ incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/EtchRouterMessageFilter.java Fri Feb 27 16:37:17 2009
@@ -0,0 +1,238 @@
+/* $Id$
+ *
+ * Copyright 2009-2010 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.
+ */
+package org.apache.etch.services.router;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.etch.bindings.java.msg.Direction;
+import org.apache.etch.bindings.java.msg.Message;
+import org.apache.etch.bindings.java.msg.Type;
+import org.apache.etch.bindings.java.transport.SessionMessage;
+import org.apache.etch.bindings.java.transport.TransportMessage;
+import org.apache.etch.services.router.ConnectionStackInfo.ConnectionType;
+import org.apache.etch.services.router.EtchRouter.EtchRouterException;
+import org.apache.etch.services.router.plugin.PluginGroup;
+import org.apache.etch.services.router.plugin.PluginMemberConnection;
+import org.apache.etch.util.Resources;
+import org.apache.etch.util.URL;
+import org.apache.etch.util.core.Who;
+
+/**
+ * 
+ * @author Wei Wang (weiwa@cisco.com)
+ *
+ */
+public class EtchRouterMessageFilter implements TransportMessage,
+	SessionMessage
+{
+	
+	private static final Logger _LOGGER = Logger.getLogger( EtchRouterMessageFilter.class.getName());
+	
+	private EtchRouterManager _etchRouterManager = null;
+	
+	private final TransportMessage _transport;
+	
+	private SessionMessage _session = null;
+
+	/**
+	 * 
+	 * @param transport
+	 * @param uri
+	 * @param resources
+	 */
+	public EtchRouterMessageFilter( TransportMessage transport, URL uri,
+		Resources resources )
+	{
+		this._transport = transport;
+		_etchRouterManager = (EtchRouterManager)resources.get( EtchRouterManager._ETCH_ROUTER_MANAGER );
+		transport.setSession( this );
+	}
+
+	@Override
+	public String toString()
+	{
+		return String.format( "EtchRouterMessageFilter/%s", _transport);
+	}
+	
+	public void transportMessage( Who recipient, Message msg ) throws Exception
+	{
+		_transport.transportMessage( recipient, msg );
+	}
+
+	public SessionMessage getSession()
+	{
+		return _session;
+	}
+
+	public void setSession( SessionMessage session )
+	{
+		_session = session;
+	}
+
+	public void transportControl( Object control, Object value )
+		throws Exception
+	{
+		_transport.transportControl( control, value );
+	}
+
+	public void transportNotify( Object event ) throws Exception
+	{
+		_transport.transportNotify( event );
+	}
+
+	public Object transportQuery( Object query ) throws Exception
+	{
+		return _transport.transportQuery( query );
+	}
+
+	public boolean sessionMessage( Who sender, Message msg ) throws Exception
+	{
+    	ConnectionStackInfo connStackInfo = _etchRouterManager.getConnectionStackInfo( this );
+    	if (connStackInfo==null)
+    	{
+    		_LOGGER.log( Level.SEVERE, "No connection stack info is saved with EtchRouter manager for {0}", this );
+    		return false;
+    	}
+		Type methodType = msg.type();
+		ConnectionType connType = connStackInfo.getConnectionType();
+		if (_etchRouterManager.isLocalMethodType( methodType ))
+		{
+			Long msgid = msg.getInReplyTo();
+			// the upper level mailbox manager will handle this message if
+			// it's a reply message
+			if (msgid == null)
+			{
+				_etchRouterManager.handleLocalMethodMessage( sender, connStackInfo, msg );
+			}
+		}
+		else if( connType==ConnectionType.APP_CLIENT_CONN )
+		{
+			try
+			{
+				handleMessageFromAppClientConnection( sender, msg, connStackInfo, methodType );
+			}
+			catch (Exception e)
+			{
+				returnException( sender, msg, e, connStackInfo);
+			}
+		}
+		else if ( connType==ConnectionType.PLUGIN_SERVER_CONN )
+		{
+			//from plugin-server -> app-client:
+			ApplicationConnectionInfo appConnInfo = _etchRouterManager.getAppConnectionInfoByPluginServerStackInfo( connStackInfo );
+			ConnectionStackInfo appClientStackInfo = appConnInfo.getApplicationClientConnectionStackInfo();
+			appClientStackInfo.getMessageFilter().transportMessage( sender, msg );
+		}
+		else if ( connType==null && methodType.getDirection()!=Direction.CLIENT)
+		{
+			try
+			{
+				_etchRouterManager.registerAnonymousApplication( connStackInfo, methodType );
+				handleMessageFromAppClientConnection( sender, msg, connStackInfo, methodType );
+			}
+			catch (Exception e)
+			{
+				returnException( sender, msg, e, connStackInfo);
+			}
+		}
+		return _session.sessionMessage( sender, msg );
+	}
+
+	private void returnException( Who sender, Message origMsg, Exception e, ConnectionStackInfo connStackInfo)
+	{
+		Type methodType = origMsg.type();
+		_LOGGER.log( Level.INFO, "Got Exception, original message type is "+methodType.getName()+": ", e);
+		Message rmsg = (methodType.getResult()==null) ? null : origMsg.reply();
+		if (rmsg==null) return;
+		if (!(e instanceof RuntimeException)) e = new RuntimeException( e );
+		rmsg.put( DynamicValueFactory._mf_result, e);
+		try
+		{
+			connStackInfo.getDeliveryService().transportMessage( sender, rmsg );
+		}
+		catch (Exception ee)
+		{
+			_LOGGER.log( Level.SEVERE, "Got Exception: ", ee);
+		}
+	}
+	
+	
+	private void handleMessageFromAppClientConnection(Who sender, Message msg, ConnectionStackInfo connStackInfo, Type methodType ) throws Exception
+	{
+		//from app-client -> plugin:
+		ApplicationConnectionInfo appConnInfo = _etchRouterManager.getAppConnectionInfoByAppClientStackInfo( connStackInfo );
+		// make sure the type info for this method is loaded with the app connection:
+		try
+		{
+			appConnInfo.loadPluginGroupByMethodType( methodType );
+		}
+		catch (Exception e)
+		{
+			// ignore this exception for we don't care whether it's a server direction'ed message
+		}
+		PluginMemberConnection pluginConn = appConnInfo.getPluginConnectionByMethod( methodType );
+		if (pluginConn==null)
+		{
+			PluginGroup pluginGrp = _etchRouterManager.getPluginGroup( methodType );
+			if (pluginGrp!=null)
+			{
+				//No plugin connection is available for this API call:
+				throw new EtchRouterException(1, String.format( "Cannot call API '%s' because the plugin connection is not established", methodType.getName() ));
+			}
+			else
+			{
+				_LOGGER.log( Level.INFO, "Not handled message from client connection: {0}", msg );
+			}
+		}
+		else
+		{
+			ERRemoteServer remoteServer = pluginConn.getRemoteServer();
+			ConnectionStackInfo serverStackInfo = remoteServer.getConnectionStackInfo();
+			serverStackInfo.getMessageFilter().transportMessage( sender, msg );
+		}
+	}
+	
+	public void sessionControl( Object control, Object value ) throws Exception
+	{
+		_session.sessionControl( control, value );
+	}
+
+	public void sessionNotify( Object event ) throws Exception
+	{
+		if (event instanceof Exception)
+		{
+			_LOGGER.log( Level.FINER, "Got an exception in sessionNotify", (Exception)event);
+		}
+		_session.sessionNotify( event );
+	   	ConnectionStackInfo connStackInfo = _etchRouterManager.getConnectionStackInfo( this );
+    	if (connStackInfo==null)
+    	{
+    		_LOGGER.log( Level.SEVERE, "No connection stack info is saved with EtchRouter manager for {0}", this );
+    	}
+    	else
+    	{
+    		_etchRouterManager.sessionNotify( event, connStackInfo );
+    	}
+	}
+
+	public Object sessionQuery( Object query ) throws Exception
+	{
+		return _session.sessionQuery( query );
+	}
+
+}

Propchange: incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/EtchRouterMessageFilter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/EtchRouterMessageFilter.java
------------------------------------------------------------------------------
    svn:keywords = "Author Date Id Revision"

Added: incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/EtchRouterMgrTransportFactory.java
URL: http://svn.apache.org/viewvc/incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/EtchRouterMgrTransportFactory.java?rev=748580&view=auto
==============================================================================
--- incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/EtchRouterMgrTransportFactory.java (added)
+++ incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/EtchRouterMgrTransportFactory.java Fri Feb 27 16:37:17 2009
@@ -0,0 +1,243 @@
+/* $Id$
+ *
+ * Copyright 2009-2010 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.
+ */
+package org.apache.etch.services.router;
+
+import java.net.Socket;
+
+import javax.net.ssl.SSLSocket;
+
+import org.apache.etch.bindings.java.msg.ValueFactory;
+import org.apache.etch.bindings.java.support.DeliveryService;
+import org.apache.etch.bindings.java.support.ServerFactory;
+import org.apache.etch.bindings.java.transport.DefaultDeliveryService;
+import org.apache.etch.bindings.java.transport.MailboxManager;
+import org.apache.etch.bindings.java.transport.Messagizer;
+import org.apache.etch.bindings.java.transport.PlainMailboxManager;
+import org.apache.etch.bindings.java.transport.TcpTransportFactory;
+import org.apache.etch.bindings.java.transport.TransportMessage;
+import org.apache.etch.util.Resources;
+import org.apache.etch.util.URL;
+import org.apache.etch.util.core.io.Packetizer;
+import org.apache.etch.util.core.io.SessionListener;
+import org.apache.etch.util.core.io.TcpConnection;
+import org.apache.etch.util.core.io.TcpListener;
+import org.apache.etch.util.core.io.TlsConnection;
+import org.apache.etch.util.core.io.TlsListener;
+import org.apache.etch.util.core.io.Transport;
+import org.apache.etch.util.core.io.TransportData;
+import org.apache.etch.util.core.io.TransportPacket;
+
+/**
+ * 
+ * @author Wei Wang (weiwa@cisco.com)
+ *
+ */
+public class EtchRouterMgrTransportFactory extends TcpTransportFactory
+{
+	
+	private static EtchRouterMgrTransportFactory _tcpTransportFactory = new EtchRouterMgrTransportFactory(false);
+	
+	private static EtchRouterMgrTransportFactory _tlsTransportFactory = new EtchRouterMgrTransportFactory(true);
+	
+	private static EtchRouterMgrTransportFactory getFactoryByScheme( String scheme, String uri ) throws Exception
+	{
+		EtchRouterMgrTransportFactory f = null;
+		if ("tcp".equalsIgnoreCase( scheme ))
+			f = _tcpTransportFactory;
+		else if ("tls".equalsIgnoreCase( scheme ))
+			f = _tlsTransportFactory;
+		else
+			throw new Exception("No transport factory found for URL " + uri);
+		return f;
+	}
+	
+	// TODO these methods should not be here. they come from Transport.
+	static public DeliveryService getTransport( String uri,
+		Resources resources ) throws Exception
+	{
+		URL u = new URL( uri );
+		EtchRouterMgrTransportFactory f = getFactoryByScheme( u.getScheme(), uri );
+		return f.newTransport( uri, resources );
+	}
+	
+	// TODO these methods should not be here. they come from Transport.
+	static public Transport<ServerFactory> getListener( String uri,
+		Resources resources ) throws Exception
+	{
+		URL u = new URL( uri );
+		EtchRouterMgrTransportFactory f = getFactoryByScheme( u.getScheme(), uri );
+		return f.newListener( uri, resources );
+	}
+
+	/**
+	 * Constructs a TcpTransportFactory which delivers TcpConnection.
+	 */
+	public EtchRouterMgrTransportFactory()
+	{
+		this( false );
+	}
+	
+	/**
+	 * Constructs a TcpTransportFactory which delivers TcpConnection or a
+	 * TlsConnection depending upon the isSecure parameter.
+	 * @param isSecure true if TlsConnection is desired, false otherwise.
+	 */
+	public EtchRouterMgrTransportFactory( boolean isSecure )
+	{
+		super(isSecure);
+		_isSecure = isSecure;
+	}
+	
+	private final static String _SOCKET = "TcpTransportFactory.socket";
+	
+	private boolean _isSecure = false;
+	
+	@Override
+	public DeliveryService newTransport( String uri, Resources resources ) throws Exception
+	{
+		EtchRouterManager etchRouterManager = (EtchRouterManager)resources.get( EtchRouterManager._ETCH_ROUTER_MANAGER );
+		
+		URL u = new URL( uri );
+		
+		Object socket = resources.get( _SOCKET );
+		
+		TransportData c = null;
+		
+		if (_isSecure)
+			c = new TlsConnection( (SSLSocket) socket, u, resources );
+		else
+			c = new TcpConnection( (Socket) socket, u, resources );
+		
+		TransportPacket p = new Packetizer( c, u, resources );
+		
+		Messagizer msg = new Messagizer( p, u, resources );
+		
+		TransportMessage m = addFilters( msg, u, resources );
+		
+		EtchRouterMessageFilter messageFilter = new EtchRouterMessageFilter(m, u, resources);
+		
+		MailboxManager r = new PlainMailboxManager( messageFilter, u, resources );
+		
+		DeliveryService d = new DefaultDeliveryService( r, u, resources );
+		
+		ValueFactory vf = (ValueFactory) resources.get( Transport.VALUE_FACTORY );
+		
+		ConnectionStackInfo connInfo = new ConnectionStackInfo( messageFilter, r, d, vf );
+		
+		etchRouterManager.addConnectionStackInfo( connInfo );
+		
+		return d;
+	}
+
+	@SuppressWarnings("unchecked")
+	@Override
+	public Transport<ServerFactory> newListener( final String uri, final Resources resources )
+		throws Exception
+	{
+		URL u = new URL( uri );
+		
+		Transport<SessionListener<Socket>> l;
+
+		if (_isSecure)
+			l = new TlsListener( u, resources );
+		else
+			l = new TcpListener( u, resources );
+		
+		return new MySessionListener( l, uri, resources );
+	}
+	
+	private class MySessionListener implements Transport<ServerFactory>, SessionListener<Socket>
+	{
+		/**
+		 * @param transport
+		 * @param uri
+		 * @param resources
+		 */
+		public MySessionListener( Transport<SessionListener<Socket>> transport, String uri, Resources resources )
+		{
+			this.transport = transport;
+			this.uri = uri;
+			this.resources = resources;
+			
+			transport.setSession( this );
+		}
+		
+		private final Transport<SessionListener<Socket>> transport;
+		
+		private final String uri;
+		
+		private final Resources resources;
+
+		public ServerFactory getSession()
+		{
+			return session;
+		}
+
+		public void setSession( ServerFactory session )
+		{
+			this.session = session;
+		}
+		
+		private ServerFactory session;
+
+		public Object transportQuery( Object query ) throws Exception
+		{
+			return transport.transportQuery( query );
+		}
+
+		public void transportControl( Object control, Object value )
+			throws Exception
+		{
+			transport.transportControl( control, value );
+		}
+
+		public void transportNotify( Object event ) throws Exception
+		{
+			transport.transportNotify( event );
+		}
+
+		public void sessionAccepted( Socket socket ) throws Exception
+		{
+			Resources r = new Resources( resources );
+			r.put( _SOCKET, socket );
+			
+			ValueFactory vf = session.newValueFactory();
+			r.put( Transport.VALUE_FACTORY, vf );
+			
+			TransportMessage t = newTransport( uri, r );
+			
+			session.newServer( uri, r, t );
+		}
+
+		public Object sessionQuery( Object query ) throws Exception
+		{
+			return session.sessionQuery( query );
+		}
+
+		public void sessionControl( Object control, Object value )
+			throws Exception
+		{
+			session.sessionControl( control, value );
+		}
+
+		public void sessionNotify( Object event ) throws Exception
+		{
+			session.sessionNotify( event );
+		}
+	}
+
+}

Propchange: incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/EtchRouterMgrTransportFactory.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/EtchRouterMgrTransportFactory.java
------------------------------------------------------------------------------
    svn:keywords = "Author Date Id Revision"

Added: incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/ImplBase.java
URL: http://svn.apache.org/viewvc/incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/ImplBase.java?rev=748580&view=auto
==============================================================================
--- incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/ImplBase.java (added)
+++ incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/ImplBase.java Fri Feb 27 16:37:17 2009
@@ -0,0 +1,32 @@
+/* $Id$
+ *
+ * Copyright 2009-2010 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.
+ */
+package org.apache.etch.services.router;
+
+public class ImplBase
+{
+	private EtchRouterManager _etchRouterManager = null;
+	
+	public ImplBase( EtchRouterManager etchRouterManager )
+	{
+		etchRouterManager = _etchRouterManager;
+	}
+	
+	public EtchRouterManager getEtchRouterManager()
+	{
+		return _etchRouterManager;
+	}
+}

Propchange: incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/ImplBase.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/ImplBase.java
------------------------------------------------------------------------------
    svn:keywords = "Author Date Id Revision"

Added: incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/ImplClient.java
URL: http://svn.apache.org/viewvc/incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/ImplClient.java?rev=748580&view=auto
==============================================================================
--- incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/ImplClient.java (added)
+++ incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/ImplClient.java Fri Feb 27 16:37:17 2009
@@ -0,0 +1,51 @@
+/* $Id$
+ *
+ * Copyright 2009-2010 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.
+ */
+package org.apache.etch.services.router;
+
+import org.apache.etch.bindings.java.support.ObjSession;
+
+public class ImplClient extends ImplBase implements ObjSession
+{
+	private ERRemoteServer _server = null;
+	
+	public ImplClient(ERRemoteServer server, EtchRouterManager etchRouterManager)
+	{
+		super(etchRouterManager);
+		_server = server;
+	}
+
+	public void _sessionControl( Object control, Object value )
+		throws Exception
+	{
+		// TODO Auto-generated method stub
+		
+	}
+
+	public void _sessionNotify( Object event ) throws Exception
+	{
+		// TODO Auto-generated method stub
+		
+	}
+
+	public Object _sessionQuery( Object query ) throws Exception
+	{
+		// TODO Auto-generated method stub
+		return null;
+	}
+	
+	
+}

Propchange: incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/ImplClient.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/ImplClient.java
------------------------------------------------------------------------------
    svn:keywords = "Author Date Id Revision"

Added: incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/ImplServer.java
URL: http://svn.apache.org/viewvc/incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/ImplServer.java?rev=748580&view=auto
==============================================================================
--- incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/ImplServer.java (added)
+++ incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/ImplServer.java Fri Feb 27 16:37:17 2009
@@ -0,0 +1,61 @@
+/* $Id$
+ *
+ * Copyright 2009-2010 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.
+ */
+package org.apache.etch.services.router;
+
+import org.apache.etch.bindings.java.support.ObjSession;
+
+/**
+ * 
+ * @author Wei Wang (weiwa@cisco.com)
+ *
+ */
+public class ImplServer extends ImplBase implements ObjSession
+{
+
+	private ERRemoteClient _client = null;
+	
+	/**
+	 * 
+	 * @param client
+	 */
+	public ImplServer(ERRemoteClient client, EtchRouterManager etchRouterManager)
+	{
+		super(etchRouterManager);
+		_client = client;
+	}
+
+	public void _sessionControl( Object control, Object value )
+		throws Exception
+	{
+		// TODO Auto-generated method stub
+		
+	}
+
+	public void _sessionNotify( Object event ) throws Exception
+	{
+		// TODO Auto-generated method stub
+		
+	}
+
+	public Object _sessionQuery( Object query ) throws Exception
+	{
+		// TODO Auto-generated method stub
+		return null;
+	}
+	
+	
+}

Propchange: incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/ImplServer.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/ImplServer.java
------------------------------------------------------------------------------
    svn:keywords = "Author Date Id Revision"

Added: incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/PluginInstallInfo.java
URL: http://svn.apache.org/viewvc/incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/PluginInstallInfo.java?rev=748580&view=auto
==============================================================================
--- incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/PluginInstallInfo.java (added)
+++ incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/PluginInstallInfo.java Fri Feb 27 16:37:17 2009
@@ -0,0 +1,115 @@
+/* $Id$
+ *
+ * Copyright 2009-2010 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.
+ */
+package org.apache.etch.services.router;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import org.apache.etch.bindings.java.msg.Type;
+
+/**
+ * Class
+ * 
+ * @author Wei Wang (weiwa@cisco.com)
+ *
+ */
+public class PluginInstallInfo
+{
+	private Properties _metaProperties = null;
+	
+	private String _name = null;
+	
+	private Map<String, Type> _methodMapByName = null;
+	
+	private Map<Integer, Type> _methodMapById = null;
+	
+	/**
+	 * Constructor
+	 * 
+	 * @param name
+	 */
+	public PluginInstallInfo(String name, Properties metaProperties)
+	{
+		_metaProperties = metaProperties;
+		_name = name;
+		_methodMapByName = new HashMap<String, Type>();
+		_methodMapById = new HashMap<Integer, Type>();
+	}
+	
+	public void addMethod(Type method)
+	{
+		if (method==null) return;
+		_methodMapByName.put( method.getName(), method );
+		_methodMapById.put( method.getId(), method );
+	}
+	
+	public Type getMethod(String name)
+	{
+		return _methodMapByName.get( name );
+	}
+	
+	public Type getMethod(Integer id)
+	{
+		return _methodMapById.get( id );
+	}
+	
+	public Collection<Type> getMethods()
+	{
+		return _methodMapById.values();
+	}
+	
+	public String getName()
+	{
+		return _name;
+	}
+	
+	public String getMetaProperty( String key )
+	{
+		return _metaProperties.getProperty( key );
+	}
+	
+	public String getMetaProperty( String key, String defValue )
+	{
+		return _metaProperties.getProperty( key, defValue );
+	}
+	
+	/**
+	 * 
+	 * @param prefix
+	 * @return
+	 */
+	public String[] getSortedMetaPropertyKeysWithPrefix( String prefix )
+	{
+		Set<Object> keySet = _metaProperties.keySet();
+		List<String> keyList = new ArrayList<String>();
+		for (Object key : keySet)
+		{
+			if (key.toString().startsWith( prefix ))
+				keyList.add( key.toString() );
+		}
+		String[] arr = new String[keyList.size()];
+		arr = keyList.toArray( arr );
+		Arrays.sort( arr );
+		return arr;
+	}
+}

Propchange: incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/PluginInstallInfo.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/PluginInstallInfo.java
------------------------------------------------------------------------------
    svn:keywords = "Author Date Id Revision"

Added: incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/plugin/FailoverPluginGroup.java
URL: http://svn.apache.org/viewvc/incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/plugin/FailoverPluginGroup.java?rev=748580&view=auto
==============================================================================
--- incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/plugin/FailoverPluginGroup.java (added)
+++ incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/plugin/FailoverPluginGroup.java Fri Feb 27 16:37:17 2009
@@ -0,0 +1,79 @@
+/* $Id$
+ *
+ * Copyright 2009-2010 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.
+ */
+package org.apache.etch.services.router.plugin;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.etch.services.router.ConnectionStackInfo;
+import org.apache.etch.services.router.EtchRouterManager;
+import org.apache.etch.services.router.PluginInstallInfo;
+import org.apache.etch.services.router.EtchRouter.EtchRouterException;
+import org.apache.etch.services.router.utils.ApplicationAttributes;
+
+
+/**
+ * 
+ * @author Wei Wang (weiwa@cisco.com)
+ *
+ */
+public class FailoverPluginGroup extends PluginGroup
+{
+	
+	private List<PluginMember> _primaryMembers = new ArrayList<PluginMember>();
+	/**
+	 * 
+	 * @param name
+	 * @param routerMgr
+	 * @param installInfo
+	 */
+	public FailoverPluginGroup( String name, EtchRouterManager routerMgr, PluginInstallInfo installInfo)
+	{
+		super( name, routerMgr, installInfo);
+	}
+
+	@Override
+	public synchronized PluginMember getActiveMember(
+		ApplicationAttributes appAttrs ) throws EtchRouterException
+	{
+		for (PluginMember aMember : _primaryMembers)
+		{
+			if (aMember.isActive() && aMember.getMetaAttributes().matches( appAttrs ))
+				return aMember;
+		}
+		PluginMember member = super.getActiveMember( appAttrs );
+		_primaryMembers.add( member );
+		return member;
+	}
+
+	@Override
+	public synchronized void onPluginMemberConnectionDown( ConnectionStackInfo connInfo )
+		throws EtchRouterException
+	{
+		super.onPluginMemberConnectionDown( connInfo );
+		for (PluginMember member : _primaryMembers)
+		{
+			if (connInfo==member.getConnInfo())
+			{
+				_primaryMembers.remove( member );
+				return;
+			}
+		}
+	}
+	
+	
+}

Propchange: incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/plugin/FailoverPluginGroup.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/plugin/FailoverPluginGroup.java
------------------------------------------------------------------------------
    svn:keywords = "Author Date Id Revision"

Added: incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/plugin/PluginGroup.java
URL: http://svn.apache.org/viewvc/incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/plugin/PluginGroup.java?rev=748580&view=auto
==============================================================================
--- incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/plugin/PluginGroup.java (added)
+++ incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/plugin/PluginGroup.java Fri Feb 27 16:37:17 2009
@@ -0,0 +1,204 @@
+/* $Id$
+ *
+ * Copyright 2009-2010 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.
+ */
+package org.apache.etch.services.router.plugin;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.etch.services.router.ApplicationConnectionInfo;
+import org.apache.etch.services.router.ConnectionStackInfo;
+import org.apache.etch.services.router.EtchRouterManager;
+import org.apache.etch.services.router.PluginInstallInfo;
+import org.apache.etch.services.router.EtchRouter.EtchRouterException;
+import org.apache.etch.services.router.utils.ApplicationAttributes;
+import org.apache.etch.services.router.utils.PluginAttributes;
+
+
+/**
+ * 
+ * @author Wei Wang (weiwa@cisco.com)
+ *
+ */
+public abstract class PluginGroup
+{
+	/**
+	 * 
+	 */
+	public static final String _URL_PROPERTY_KEY_PREFIX = "plugin.member.url.";
+	
+	/**
+	 * 
+	 */
+	public static final String _META_DATA_PROPERTY_KEY_PREFIX = "plugin.member.metadata.";
+	
+	/**
+	 * Factory method to new a PluginGroup of a particular type
+	 * 
+	 * @param name
+	 * @param routerMgr
+	 * @param installInfo
+	 * @return
+	 */
+	public static PluginGroup newPluginGroup( String name, EtchRouterManager routerMgr, PluginInstallInfo installInfo)
+	{
+		String groupType = installInfo.getMetaProperty( "plugin.group.type" );
+		PluginGroup group = null;
+		if ("roundrobin".equalsIgnoreCase( groupType ))
+		{
+			group = new RoundRobinPluginGroup(name, routerMgr, installInfo);
+		}
+		if (group==null)
+			group = new FailoverPluginGroup(name, routerMgr, installInfo);
+		return group;
+	}
+	
+	protected String _name = null;
+	
+	protected List<PluginMember> _pluginMembers = null;
+	
+	protected PluginInstallInfo _installInfo = null;
+	
+	protected EtchRouterManager _etchRouterManager = null;
+	
+	protected List<ApplicationConnectionInfo> _appConnections = null;
+	
+	protected PluginGroup(String name, EtchRouterManager routerMgr, PluginInstallInfo installInfo)
+	{
+		_name = name;
+		_pluginMembers = new ArrayList<PluginMember>();
+		_etchRouterManager = routerMgr;
+		_installInfo = installInfo;
+		_appConnections = new ArrayList<ApplicationConnectionInfo>();
+		populatePluginMembers();
+	}
+	
+	private void populatePluginMembers()
+	{
+		String[] urlKeys = _installInfo.getSortedMetaPropertyKeysWithPrefix( _URL_PROPERTY_KEY_PREFIX );
+		for (String key : urlKeys)
+		{
+			String url = _installInfo.getMetaProperty( key );
+			String suffix = key.substring( _URL_PROPERTY_KEY_PREFIX.length() );
+			String metaData = _installInfo.getMetaProperty( String.format( "%s%s", _META_DATA_PROPERTY_KEY_PREFIX, suffix ) );
+			PluginMember member = new PluginMember(this, url, metaData);
+			_pluginMembers.add( member );
+		}
+	}
+	
+	/**
+	 * Getter
+	 * 
+	 * @return
+	 */
+	public String getName()
+	{
+		return _name;
+	}
+	
+	/**
+	 * 
+	 * @return
+	 * @throws EtchRouterException
+	 */
+	public synchronized PluginMember getActiveMember( ApplicationAttributes appAttrs ) throws EtchRouterException
+	{
+		if (_pluginMembers.isEmpty())
+			throw new EtchRouterException(1, String.format( "No member is registered in plugin group '%s'", getName()));
+		for (PluginMember member : _pluginMembers)
+		{
+			if (member.isActive() && member.getMetaAttributes().matches( appAttrs ))
+				return member;
+		}
+		throw new EtchRouterException(1, String.format( "No active member is found in plugin group '%s' that matches application attributes \"%s\"", getName(), appAttrs.getEncodedString()));
+	}
+	
+	/**
+	 * 
+	 * @param appConnection
+	 */
+	public synchronized void addApplicationConnection( ApplicationConnectionInfo appConnection )
+	{
+		_appConnections.add( appConnection );
+	}
+	
+	/**
+	 * 
+	 */
+	public void refreshAppConnections()
+	{
+		for (ApplicationConnectionInfo appConnection : _appConnections)
+		{
+			appConnection.initPluginConnectionIfNotConnected( this );
+		}
+	}
+	
+	/**
+	 * 
+	 * @param appConnection
+	 */
+	public synchronized void removeApplicationConnection( ApplicationConnectionInfo appConnection )
+	{
+		_appConnections.remove( appConnection );
+	}
+	
+	/**
+	 * 
+	 * @param connInfo
+	 * @throws EtchRouterException
+	 */
+	public void onPluginMemberConnectionDown( ConnectionStackInfo connInfo ) throws EtchRouterException
+	{
+		if (connInfo==null) return;
+		for (PluginMember member : _pluginMembers)
+		{
+			if (connInfo==member.getConnInfo())
+			{
+				member.onRemoteConnectionDown( );
+			}
+		}
+	}
+
+	/**
+	 * 
+	 * @return
+	 */
+	public PluginInstallInfo getInstallInfo()
+	{
+		return _installInfo;
+	}
+	
+	public List<PluginMember> getPluginMembers()
+	{
+		List<PluginMember> list = new ArrayList<PluginMember>();
+		list.addAll( _pluginMembers );
+		return list;
+	}
+	
+	protected List<PluginMember> getActivePluginMembers( ApplicationAttributes appAttrs )
+	{
+		List<PluginMember> list = new ArrayList<PluginMember>(_pluginMembers.size());
+		for (PluginMember member : _pluginMembers)
+		{
+			if (member.isActive()) {
+				PluginAttributes pluginAttrs = member.getMetaAttributes();
+				if (pluginAttrs.matches( appAttrs ))
+					list.add(member);
+			}
+		}
+		return list;
+	}
+}

Propchange: incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/plugin/PluginGroup.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/plugin/PluginGroup.java
------------------------------------------------------------------------------
    svn:keywords = "Author Date Id Revision"

Added: incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/plugin/PluginMember.java
URL: http://svn.apache.org/viewvc/incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/plugin/PluginMember.java?rev=748580&view=auto
==============================================================================
--- incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/plugin/PluginMember.java (added)
+++ incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/plugin/PluginMember.java Fri Feb 27 16:37:17 2009
@@ -0,0 +1,166 @@
+/* $Id$
+ *
+ * Copyright 2009-2010 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.
+ */
+package org.apache.etch.services.router.plugin;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.etch.services.router.ConnectionStackInfo;
+import org.apache.etch.services.router.ERRemoteServer;
+import org.apache.etch.services.router.EtchRouterManager;
+import org.apache.etch.services.router.EtchRouterManagerHelper;
+import org.apache.etch.services.router.utils.PluginAttributes;
+
+
+/**
+ * 
+ * @author Wei Wang (weiwa@cisco.com)
+ *
+ */
+public class PluginMember
+{
+	private static final Logger _LOGGER = Logger.getLogger( PluginMember.class.getName());
+	
+	private PluginGroup _pluginGroup = null;
+	
+	private String _url = null;
+	private String _metaData = null;
+	private PluginAttributes _metaAttrs = null;
+	private List<PluginMemberConnection> _memberConnections = null;
+	
+	private ERRemoteServer _remoteServer = null;
+	
+	private boolean _active = false;
+	
+	/**
+	 * 
+	 * @param pluginGroup
+	 * @param url
+	 * @param metaData
+	 */
+	public PluginMember( PluginGroup pluginGroup, String url, String metaData )
+	{
+		_pluginGroup = pluginGroup;
+		_url = url;
+		_metaData = metaData;
+		_memberConnections = Collections.synchronizedList( new ArrayList<PluginMemberConnection>());
+		try
+		{
+			_metaAttrs = new PluginAttributes(_pluginGroup.getName(), _metaData);
+		}
+		catch (Exception e)
+		{
+			String msg = String.format( "Invalid plugin member metaData \"%s\"", _metaData);
+			_LOGGER.log( Level.SEVERE, msg, e );
+			try
+			{
+				_metaAttrs = new PluginAttributes(_pluginGroup.getName(), null);
+			}
+			catch (Exception ee)
+			{
+				_LOGGER.log( Level.SEVERE, "Got exception with NULL plugin member metaData: ", e );
+			}
+		}
+	}
+
+	public PluginAttributes getMetaAttributes()
+	{
+		return _metaAttrs;
+	}
+	
+	public PluginGroup getPluginGroup()
+	{
+		return _pluginGroup;
+	}
+	
+	public String getUrl()
+	{
+		return _url;
+	}
+
+	public String getMetaData()
+	{
+		return _metaData;
+	}
+	
+	public ConnectionStackInfo getConnInfo()
+	{
+		return (_remoteServer==null ? null : _remoteServer.getConnectionStackInfo());
+	}
+	
+	public void addMemberConnection(PluginMemberConnection memberConn)
+	{
+		_memberConnections.add( memberConn );
+	}
+	
+	public void removeMemberConnection(PluginMemberConnection memberConn)
+	{
+		_memberConnections.remove( memberConn );
+	}
+	
+	public int sizeOfMemberConnections()
+	{
+		return _memberConnections.size();
+	}
+
+	public synchronized void checkState(EtchRouterManager etchRouterManager)
+	{
+		if (_active) return;
+		ConnectionStackInfo connInfo = null;
+		try
+		{
+			_remoteServer = EtchRouterManagerHelper.newServer( etchRouterManager, _url, null, _pluginGroup.getName() );
+			connInfo = _remoteServer.getConnectionStackInfo();
+			connInfo.setConnectionType( ConnectionStackInfo.ConnectionType.PLUGIN_MONITOR_CONN, _pluginGroup.getName() );
+			_remoteServer._startAndWaitUp( etchRouterManager.getConnectionStartMaxDelay() );
+			_active = true;
+			etchRouterManager.addConnectionStackInfo( connInfo );
+			_LOGGER.log( Level.INFO, "Succefully created a monitor connection to plugin service url \"{0}\", the monitor connection is \"{1}\"", new Object[]{_url, _remoteServer.toString()} );
+		}
+		catch (Exception e)
+		{
+			_LOGGER.log( Level.FINER, String.format( "Failed to connection to plugin service url \"%s\"", _url), e );
+			_remoteServer = null;
+			if (connInfo != null) etchRouterManager.removeConnectionStackInfo( connInfo );
+		}
+	}
+	
+	public synchronized void onRemoteConnectionDown()
+	{
+		_active = false;
+		if (_remoteServer!=null)
+		{
+			_remoteServer = null;
+		}
+	}
+	
+	public synchronized boolean isActive()
+	{
+		return _active;
+	}
+	
+	@Override
+	public String toString()
+	{
+		return String.format( "%s[url:%s, meta-data:%s]", this.getClass().getSimpleName(), _url, _metaData );
+	}
+	
+	
+}
\ No newline at end of file

Propchange: incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/plugin/PluginMember.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/plugin/PluginMember.java
------------------------------------------------------------------------------
    svn:keywords = "Author Date Id Revision"

Added: incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/plugin/PluginMemberConnection.java
URL: http://svn.apache.org/viewvc/incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/plugin/PluginMemberConnection.java?rev=748580&view=auto
==============================================================================
--- incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/plugin/PluginMemberConnection.java (added)
+++ incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/plugin/PluginMemberConnection.java Fri Feb 27 16:37:17 2009
@@ -0,0 +1,52 @@
+/* $Id$
+ *
+ * Copyright 2009-2010 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.
+ */
+package org.apache.etch.services.router.plugin;
+
+import org.apache.etch.services.router.ERRemoteServer;
+
+public class PluginMemberConnection
+{
+
+	private PluginMember _member = null;
+	
+	private ERRemoteServer _remoteServer = null;
+	
+	public PluginMemberConnection( PluginMember member, ERRemoteServer remoteServer )
+	{
+		_member = member;
+		_remoteServer = remoteServer;
+	}
+
+	public PluginMember getMember()
+	{
+		return _member;
+	}
+
+	public ERRemoteServer getRemoteServer()
+	{
+		return _remoteServer;
+	}
+
+	@Override
+	public String toString()
+	{
+		return String.format( "%s[member:%s, remote-server:%s]", this.getClass().getSimpleName(), _member, _remoteServer );
+	}
+	
+	
+	
+}

Propchange: incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/plugin/PluginMemberConnection.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/plugin/PluginMemberConnection.java
------------------------------------------------------------------------------
    svn:keywords = "Author Date Id Revision"

Added: incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/plugin/PluginStateMonitor.java
URL: http://svn.apache.org/viewvc/incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/plugin/PluginStateMonitor.java?rev=748580&view=auto
==============================================================================
--- incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/plugin/PluginStateMonitor.java (added)
+++ incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/plugin/PluginStateMonitor.java Fri Feb 27 16:37:17 2009
@@ -0,0 +1,124 @@
+/* $Id$
+ *
+ * Copyright 2009-2010 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.
+ */
+package org.apache.etch.services.router.plugin;
+
+import java.util.Collection;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.etch.services.router.EtchRouterManager;
+
+
+/**
+ * 
+ * @author Wei Wang (weiwa@cisco.com)
+ *
+ */
+public class PluginStateMonitor implements Runnable
+{
+	private static final Logger _LOGGER = Logger.getLogger( PluginStateMonitor.class.getName());
+	
+	private int _sleepTime = 60000;
+	
+	private EtchRouterManager _etchRouterManager = null;
+	
+	private boolean _running = true;
+	
+	/**
+	 * 
+	 * @param etchRouterManager
+	 */
+	public PluginStateMonitor(EtchRouterManager etchRouterManager)
+	{
+		_etchRouterManager = etchRouterManager;
+		try
+		{
+			_sleepTime = Integer.parseInt( _etchRouterManager.getProperty( "plugin.monitor.sleep.interval", "" ) );
+		}
+		catch (Exception e)
+		{
+		}
+	}
+	
+	/**
+	 * 
+	 */
+	public void stop()
+	{
+		_running = false;
+	}
+	
+	public void run()
+	{
+		int sleepBetween = 2*_etchRouterManager.getConnectionStartMaxDelay();
+		while (_running)
+		{
+			_LOGGER.log( Level.FINE, "Checking each plugin member's state..." );
+			Collection<PluginGroup> pluginGrps = _etchRouterManager.getPluginGroups();
+			for (PluginGroup pluginGrp : pluginGrps)
+			{
+				Collection<PluginMember> members = pluginGrp.getPluginMembers();
+				for (PluginMember member : members)
+				{
+					Thread runner = new Thread(new CheckPluginMemberStateRunner(member));
+					runner.start();
+				}
+			}
+			
+			_LOGGER.log( Level.FINE, "Sleeping for {0} miliseconds before refreshing applications...", sleepBetween );
+			try
+			{
+				Thread.sleep( sleepBetween );
+			}
+			catch (Exception e)
+			{
+				
+			}
+			
+			_LOGGER.log( Level.FINE, "Refreshing each application's connection..." );
+			for (PluginGroup pluginGrp : pluginGrps)
+			{
+				pluginGrp.refreshAppConnections();
+			}
+			
+			_LOGGER.log( Level.FINE, "Sleeping for {0} miliseconds before starting next cycle...", _sleepTime );
+			try
+			{
+				Thread.sleep( _sleepTime );
+			}
+			catch (Exception e)
+			{
+			}
+		}
+	}
+	
+	private class CheckPluginMemberStateRunner implements Runnable
+	{
+		private PluginMember _pluginMember = null;
+		
+		public CheckPluginMemberStateRunner(PluginMember member)
+		{
+			_pluginMember = member;
+		}
+		
+		public void run()
+		{
+			_pluginMember.checkState( _etchRouterManager );
+		}
+	}
+
+}

Propchange: incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/plugin/PluginStateMonitor.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/etch/branches/router/services/router/src/main/java/org/apache/etch/services/router/plugin/PluginStateMonitor.java
------------------------------------------------------------------------------
    svn:keywords = "Author Date Id Revision"