All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.activecomponents.webservice.AbstractWebSocketServer Maven / Gradle / Ivy

There is a newer version: 4.0.267
Show newest version
package org.activecomponents.webservice;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.activecomponents.webservice.messages.BaseMessage;
import org.activecomponents.webservice.messages.PartialMessage;
import org.activecomponents.webservice.messages.PullResultMessage;
import org.activecomponents.webservice.messages.ResultMessage;
import org.activecomponents.webservice.messages.ServiceInvocationMessage;
import org.activecomponents.webservice.messages.ServiceProvideMessage;
import org.activecomponents.webservice.messages.ServiceSearchMessage;
import org.activecomponents.webservice.messages.ServiceTerminateInvocationMessage;
import org.activecomponents.webservice.messages.ServiceUnprovideMessage;

import jadex.base.Starter;
import jadex.bridge.IComponentIdentifier;
import jadex.bridge.IComponentStep;
import jadex.bridge.IExternalAccess;
import jadex.bridge.IInternalAccess;
import jadex.bridge.SFuture;
import jadex.bridge.sensor.service.TagProperty;
import jadex.bridge.service.IService;
import jadex.bridge.service.IServiceIdentifier;
import jadex.bridge.service.ServiceScope;
import jadex.bridge.service.component.IProvidedServicesFeature;
import jadex.bridge.service.search.ServiceQuery;
import jadex.bridge.service.types.cms.CreationInfo;
import jadex.bridge.service.types.library.ILibraryService;
import jadex.bridge.service.types.serialization.ISerializationServices;
import jadex.commons.FileFilter;
import jadex.commons.IFilter;
import jadex.commons.MethodInfo;
import jadex.commons.SClassReader.AnnotationInfo;
import jadex.commons.SClassReader.ClassFileInfo;
import jadex.commons.SClassReader.ClassInfo;
import jadex.commons.SReflect;
import jadex.commons.SUtil;
import jadex.commons.collection.MultiCollection;
import jadex.commons.future.DefaultResultListener;
import jadex.commons.future.DelegationResultListener;
import jadex.commons.future.ExceptionDelegationResultListener;
import jadex.commons.future.Future;
import jadex.commons.future.IFuture;
import jadex.commons.future.IIntermediateFuture;
import jadex.commons.future.IPullIntermediateFuture;
import jadex.commons.future.IResultListener;
import jadex.commons.future.ITerminableFuture;
import jadex.commons.future.ITerminableIntermediateFuture;
import jadex.commons.future.IntermediateEmptyResultListener;
import jadex.commons.future.IntermediateFuture;
import jadex.commons.transformation.BasicTypeConverter;
import jadex.commons.transformation.IStringConverter;
import jadex.micro.MinimalAgent;
import jadex.micro.annotation.Agent;
import jadex.micro.annotation.ProvidedServices;

/**
 *  The abstract websocket server handles websocket requests from clients like browsers.
 *  
 *  Is the base class for specific impls like nano or jetty.
 */
public abstract class AbstractWebSocketServer
{
	/** The platform. */
	protected IExternalAccess agent;
	
	/** The ongoing future calls from client. */
	protected Map> incalls = new HashMap>();
	
	/** The ongoing future calls to the client. */
	protected Map> outcalls = new HashMap>();
	
	/** The partial messages. 
	    todo: cleanup per timeout */
	protected Map> partials;

	/** debug flag (print stacktraces of unsuccessful service calls etc) **/
	//protected boolean debug;
	
	/** The basic type converters. */
	protected BasicTypeConverter basicconverters;
	
	/** The interface -> impl file mappings. */
	protected MultiCollection mappings;
	
	/** The methodinfos per service interface. */
	protected Map serviceinfos;
	
	/** 
	 *  Creates the server.
	 */
	public AbstractWebSocketServer(IExternalAccess agent)
	{
		this.agent = agent;
		this.basicconverters = new BasicTypeConverter();
		
		this.partials = new HashMap>();
		//this.websockets = Collections.synchronizedMap(new HashMap<>());
		
		this.serviceinfos = new HashMap<>();
	}
	
	/**
	 *  Get the platform.
	 *  @return The platform.
	 */
	public IExternalAccess getPlatform()
	{
		return agent;
	}
	
	/**
	 *  Handle a search service method.
	 *  @param session The session.
	 */
	protected IFuture handleSearchServiceMessage(final Object session, final ServiceSearchMessage ssc)
	{
		final Future ret = new Future();
		
		// Check if search type is set
		if(ssc.getType()==null || ssc.getType().getTypeName()==null || ssc.getType().getTypeName().length()==0)
		{
			Exception e = new RuntimeException("Service type must not be null in service search");
			sendException(e, ssc.getCallid(), session).addResultListener(new DelegationResultListener(ret));
			return ret;
		}
		
//		final ServiceSearchMessage ssc = (ServiceSearchMessage)msg;
//		IComponentManagementService cms = SServiceProvider.getService(platform, IComponentManagementService.class, ServiceScope.PLATFORM).get();
		//final Class type = ssc.getType().getType(NanoWebsocketServer.class.getClassLoader()); // todo: support default loader when using null
//		if(type==null)
//		{
//			Exception e = new RuntimeException("Service class not found: "+ssc.getType());
//			sendException(e, ssc.getCallid(), session).addResultListener(new DelegationResultListener(ret));
//			return ret;
//		}
		
		// default global or network?
		String scope = ssc.getScope()!=null? ssc.getScope(): ServiceScope.GLOBAL.name();
//		System.out.println("Search service with scope: "+scope);
		
		if(!ssc.isMultiple())
		{
			if("session".equals(scope))
			{
//				IService service = (IService)session.getUserProperties().get(type.getName());
				findModelName(ssc.getType().getTypeName()).then(filename ->
				{
					if(filename==null)
					{
						sendException(new RuntimeException("Could not create session component for service, no suitable ws_serviceimpl_ context parameter defined in web.xml "+ssc.getType()), ssc.getCallid(), session).addResultListener(new DelegationResultListener(ret));
					}
					else
					{
						getOrCreateSessionComponent(filename, session, filename, true).
							addResultListener(new ExceptionDelegationResultListener(ret)
						{
							public void customResultAvailable(IExternalAccess access) throws Exception
							{
								IFuture res = (IFuture)access.searchService(new ServiceQuery(ssc.getType()).setScope(ServiceScope.COMPONENT_ONLY));
								res.addResultListener(new ExceptionDelegationResultListener(ret)
								{
									public void customResultAvailable(IService service)
									{
										sendResult(service, ssc.getCallid(), session, true).addResultListener(new DelegationResultListener(ret));
									}
								});
							}
						});
					}
				}).catchEx(ret);
				//}).exceptionally(e -> {ret.setException(e); return IFuture.DONE;});
			}
			else
			{
				IFuture res = (IFuture)getPlatform().searchService(new ServiceQuery(ssc.getType()).setScope(ServiceScope.getEnum(scope)));
				res.addResultListener(new ExceptionDelegationResultListener(ret)
				{
					public void customResultAvailable(IService service)
					{
						// Found service and now send back id for generating javascript proxy
//						addService(service);
//						sendResult(getServiceInfo(service), ssc.getCallid(), session, true).addResultListener(new DelegationResultListener(ret));
						sendResult(service, ssc.getCallid(), session, true).addResultListener(new DelegationResultListener(ret));
					}
				});
			}
		}
		else
		{
			ITerminableIntermediateFuture res = (ITerminableIntermediateFuture)getPlatform().searchServices(new ServiceQuery(ssc.getType()).setScope(ServiceScope.getEnum(scope)));
			res.addResultListener(new IntermediateEmptyResultListener()
			{
				public void intermediateResultAvailable(IService service)
				{
					System.out.println("Found service: "+service.getServiceId());
//					addService(service);
					// End of call with finished() thus no addResultListener()
					sendResult(service, ssc.getCallid(), session, false);//.addResultListener(new DelegationResultListener(ret));
//							sendResult(getServiceInfo(service), ssc.getCallid(), session, false);//.addResultListener(new DelegationResultListener(ret));
				}
				
				public void resultAvailable(Collection services)
				{
					for(IService service: services)
					{
						intermediateResultAvailable(service);
					}
					finished();
				}
				
				public void finished()
				{
					sendResult(null, ssc.getCallid(), session, true).addResultListener(new DelegationResultListener(ret));
				}
				
				public void exceptionOccurred(Exception ex)
				{
					sendException(ex, ssc.getCallid(), session).addResultListener(new DelegationResultListener(ret));
				}
			});
		}
		
		return ret;
	}
	
	/**
	 *  Handle a service invocation method.
	 *  @param session The session.
	 *  @param sim The message.
	 */
	protected IFuture handleServiceInvocationMessage(final Object session, final ServiceInvocationMessage sim)
	{
		Future ret = new Future<>();
		MethodInfo[] mis = serviceinfos.get(sim.getServiceId().getServiceType());
		
		int psize = sim.getParameterValues().length;
		List fit = new ArrayList<>();
		for(MethodInfo mi: mis)
		{
			if(mi.getName().equals(sim.getMethodName()) && psize==mi.getParameterTypeInfos().length)
			{
				fit.add(mi);
			}
		}
		
		if(fit.size()==0)
		{
			sendException(new RuntimeException("Method not found: "+sim.getMethodName()), sim.getCallid(), session).addResultListener(new DelegationResultListener(ret));
			return ret;
		}
//		if(fit.size()>1)
//		{
//			sendException(new RuntimeException("Too many methods found: "+sim.getMethodName()), sim.getCallid(), session).addResultListener(new DelegationResultListener(ret));
//			return ret;
//		}
		
		MethodInfo mi = fit.get(0);
		
		//System.out.println("invokeServiceMethod: "+servicetype+" "+methodname+" "+Arrays.toString(args)+" "+rettype);
		//System.out.println("Searching service: "+sim.getServiceId()+" on platform: "+getPlatform().getId());
		
		IFuture fut = getPlatform().searchService(new ServiceQuery<>((Class)null).setServiceIdentifier(sim.getServiceId()));
		
		fut.addResultListener(new ExceptionDelegationResultListener(ret)
		{
			public void customResultAvailable(IService service) throws Exception
			{
				// Do not pass argtypes when multiple methods match
				IFuture res = service.invokeMethod(sim.getMethodName(), fit.size()==1? mi.getParameterTypeInfos(): null, sim.getParameterValues(), mi.getReturnTypeInfo());

				incalls.put(sim.getCallid(), (IFuture)res);
//				System.out.println("saving: "+sim.getCallid());

				if(res instanceof IIntermediateFuture)
				{
					((IIntermediateFuture)res).addResultListener(new IntermediateEmptyResultListener()
					{
						public void intermediateResultAvailable(Object result)
						{
//							System.out.println("ires: "+result+" "+sim.getCallid());
							sendResult(result, sim.getCallid(), session, false);//.addResultListener(new DelegationResultListener(ret));
						}

						public void exceptionOccurred(Exception e)
						{
							sendException(e, sim.getCallid(), session).addResultListener(new DelegationResultListener(ret));
							incalls.remove(sim.getCallid());
//							System.out.println("removed ex: "+sim.getCallid());
						}

						public void finished()
						{
							sendResult(null, sim.getCallid(), session, true).addResultListener(new DelegationResultListener(ret));
							incalls.remove(sim.getCallid());
//							System.out.println("removed fin: "+sim.getCallid());
						}

						public void resultAvailable(Collection result)
						{
							sendResult(result, sim.getCallid(), session, true).addResultListener(new DelegationResultListener(ret));
							incalls.remove(sim.getCallid());
//							System.out.println("removed ra: "+sim.getCallid());
						}
					});
				}
				else //if(res instanceof IFuture)
				{
					((IFuture)res).addResultListener(new IResultListener()
					{
						public void resultAvailable(Object result)
						{
							sendResult(result, sim.getCallid(), session, true).addResultListener(new DelegationResultListener(ret));
							incalls.remove(sim.getCallid());
//							System.out.println("remove fut call res: "+sim.getCallid());
						}

						public void exceptionOccurred(Exception e)
						{
							sendException(e, sim.getCallid(), session).addResultListener(new DelegationResultListener(ret));
							incalls.remove(sim.getCallid());
//							System.out.println("remove fut call ex: "+sim.getCallid());
						}
					});
				}
			}
		});
		
		return ret;
	}
	
	/**
	 *  Find the component model name for a type/interface name.
	 *  @param typename The service type name.
	 *  @return The model name.
	 */
	protected IFuture findModelName(String typename)
	{	
		Future ret = new Future<>();
		
		getMappings().then(c -> 
		{
			Collection filenames = mappings.getCollection(typename);
			
			System.out.println("Mappings for: "+typename+" "+filenames);
			
			if(filenames.size()>0)
				ret.setResult(filenames.iterator().next());
			else
				ret.setException(new RuntimeException("No mapping found for: "+typename));
		}).catchEx(ret);
		
		return ret;
	}
	
	/**
	 *  Generate a map of service interfaces and components. Helps
	 *  to find a component that implements a service interface.
	 */
	protected IFuture> getMappings()
	{
		final Future> ret = new Future<>();
		
		if(mappings==null)
		{
			mappings = new MultiCollection<>(new HashMap<>(), HashSet.class);
		
			getPlatform().scheduleStep(p ->
			{
				ILibraryService ls = p.getLocalService(ILibraryService.class);
				URL[] urls = ls.getAllURLs().get().toArray(new URL[0]);
				urls = SUtil.removeSystemUrls(urls);
				
				Set cis = new HashSet<>();
				FileFilter ff = new FileFilter(null, false, ".class");
				
				Set allcis = SReflect.scanForClassFileInfos(urls, ff, new IFilter()
				{
					public boolean filter(ClassFileInfo ci)
					{
						AnnotationInfo ai = ci.getClassInfo().getAnnotation(Agent.class.getName());
						
						if(ai!=null)
						{
							// todo: check interfaces for @Service annotation
							
							List ifaces = ci.getClassInfo().getInterfaceNames();
							
							for(String iface: ifaces)
							{
								mappings.add(iface, ci.getFilename());
							}
							
							AnnotationInfo pss = ci.getClassInfo().getAnnotation(ProvidedServices.class.getName());
							if(pss!=null)
							{
								Object[] ps = (Object[])pss.getValue("value");
								for(Object o: ps)
								{
									AnnotationInfo p = (AnnotationInfo)o;
									ClassInfo iface = (ClassInfo)p.getValue("type");
									mappings.add(iface.getClassName(), ci.getFilename());
								}
							}
						}
						
						return true;
						//return ai!=null;
					}
				});
				
				//System.out.println("Found classes: "+allcis);
				
				ret.setResult(mappings);
				
				return IFuture.DONE;
			});
		}
		else
		{
			ret.setResult(mappings);
		}
		
		return ret;
	}
	
	/**
	 *  Handle a provide service method. 
	 *  Create or get the minimal agent that publishes the service using a proxy. 
	 *  @param session The session of the provider.
	 *  @param sim The message.
	 */
	protected IFuture handleServiceProvideMessage(Object session, final ServiceProvideMessage spm)
	{
		final Future ret = new Future();
	
		getOrCreateSessionComponent("minimalagent", session, MinimalAgent.class.getName()+".class", true)
			.addResultListener(new ExceptionDelegationResultListener(ret)
		{
			public void customResultAvailable(IExternalAccess ma) throws Exception
			{
				ma.scheduleStep(new IComponentStep()
				{
					public IFuture execute(final IInternalAccess ia)
					{
						final Class sertype = spm.getType().getType(ia.getClassLoader());

//						boolean reuse = false; // spm.isReuse();
//						ia.getComponentFeature(IProvidedServicesFeature.class).getProvidedService(clazz)

						final IServiceIdentifier[] mysid = new IServiceIdentifier[1];

						Object service = Proxy.newProxyInstance(ia.getClassLoader(), new Class[]{sertype}, new InvocationHandler()
						{
							public Object invoke(Object proxy, java.lang.reflect.Method method, Object[] args) throws Throwable
							{
								// A service call was received by the service proxy of the client service
								// -> Call the client side and wait for result

//								System.out.println("Received service invocation: "+method.getName());

								Future ret = null;
								
								if(!(SReflect.isSupertype(IFuture.class, method.getReturnType())))
									throw new RuntimeException("Synchronous service methods not supported: "+method.getName());
								
								ret = SFuture.getFuture(method.getReturnType());
								
								String callid = SUtil.createUniqueId(method.getName());
								IServiceIdentifier serviceid = mysid[0];
								ServiceInvocationMessage message = new ServiceInvocationMessage(callid, serviceid, method.getName(), args);
									
								// todo: cleanup old messages
								
								outcalls.put(callid, ret);
									
								sendMessage(message, session);
								
								return ret;
							}
						});
						
						ia.getFeature(IProvidedServicesFeature.class).addService(null, sertype, service)
							.addResultListener(new ExceptionDelegationResultListener(ret)
						{
							public void customResultAvailable(Void result) throws Exception
							{
								IService ser = (IService)ia.getFeature(IProvidedServicesFeature.class).getProvidedService(sertype);
								final IServiceIdentifier sid = ser.getServiceId();
								mysid[0] = sid;

								String[] tags = spm.getTags();
								if(tags!=null && tags.length>0)
								{
									Map params = new HashMap();
									params.put(TagProperty.NAME, tags);
									TagProperty tag = new TagProperty(ia, null, null, params);
									ia.addNFProperty(sid, tag).addResultListener(new ExceptionDelegationResultListener(ret)
									{
										public void customResultAvailable(Void result) throws Exception
										{
											sendResult(sid, spm.getCallid(), session, true).addResultListener(new DelegationResultListener(ret));
										}

										public void exceptionOccurred(Exception exception)
										{
											exception.printStackTrace();
											super.exceptionOccurred(exception);
										}
									});
								}
								else
								{
									System.out.println("no tags");
									sendResult(sid, spm.getCallid(), session, true).addResultListener(new DelegationResultListener(ret));
								}
							}
						});
						return IFuture.DONE;
					}
				});
			}
			
			public void exceptionOccurred(Exception exception)
			{
				sendException(exception, spm.getCallid(), session).addResultListener(new DelegationResultListener(ret));
				super.exceptionOccurred(exception);
			}
		});
		
		return ret;
	}

	/**
	 *  Handle a provide service method.
	 *  Create or get the minimal agent that publishes the service using a proxy.
	 *  @param session The session of the provider.
	 *  @param sim The message.
	 */
	protected IFuture handleServiceUnprovideMessage(final Object session, final ServiceUnprovideMessage sum)
	{
		final Future ret = new Future();

		getOrCreateSessionComponent("minimalagent", session, MinimalAgent.class.getName()+".class", false)
			.addResultListener(new ExceptionDelegationResultListener(ret)
		{
			public void customResultAvailable(IExternalAccess ma) throws Exception
			{
				ma.scheduleStep(new IComponentStep()
				{
					public IFuture execute(final IInternalAccess ia)
					{
						ia.getFeature(IProvidedServicesFeature.class).removeService(sum.getServiceId())
							.addResultListener(new ExceptionDelegationResultListener(ret)
						{
							public void customResultAvailable(Void result) throws Exception
							{
								sendResult(sum.getServiceId(), sum.getCallid(), session, true).addResultListener(new DelegationResultListener(ret));
							}

							public void exceptionOccurred(Exception exception)
							{
								sendException(exception, sum.getCallid(), session).addResultListener(new DelegationResultListener(ret));
								super.exceptionOccurred(exception);
							}
						});
						return IFuture.DONE;
					}
				});
			}

			public void exceptionOccurred(Exception exception)
			{
				sendException(exception, sum.getCallid(), session).addResultListener(new DelegationResultListener(ret));
				super.exceptionOccurred(exception);
			}
		});

		return ret;
	}

	/**
	 *  Handle a terminate invocation method.
	 *  @param session The session.
	 *  @param sim The message.
	 */
	protected IFuture handleTerminateInvocationMessage(final Object session, final ServiceTerminateInvocationMessage stim)
	{
		final Future ret = new Future();
	
//		final ServiceTerminateInvocationMessage stim = (ServiceTerminateInvocationMessage)msg;
		
//		System.out.println("Abort: "+stim);
		
		IFuture fut = incalls.get(stim.getCallid());
		if(fut instanceof ITerminableFuture)
		{
			((ITerminableFuture)fut).terminate();
			System.out.println("Aborted: "+stim);
		}
		else
		{
			System.out.println("Cannot termiate call: "+fut+" "+stim.getCallid());
		}
		
		return ret;
	}
	
	/**
	 *  Handle a result message from a call to the client. 
	 *  @param session The session.
	 *  @param rm The message.
	 */
	protected IFuture handleResultMessage(final Object session, final ResultMessage rm)
	{
		Future fut = (Future)outcalls.get(rm.getCallid());
		
		if(rm.getException()!=null)
		{
			fut.setException(rm.getException());
		}
		else if(fut instanceof IntermediateFuture)
		{
			if(!rm.isFinished())
			{
				((IntermediateFuture)fut).addIntermediateResult(rm.getResult());
			}
			else if(rm.getResult()!=null)
			{
				((IntermediateFuture)fut).setFinished();
			}
			else
			{
				fut.setResult(rm.getResult());
			}
		}
		else if(rm.isFinished())
		{
			fut.setResult(rm.getResult());
		}
		
		if(rm.isFinished())
			outcalls.remove(rm.getCallid());
		
		return new Future((String)null);
	}
	
	/**
	 *  Handle a pull result method.
	 *  @param session The session.
	 *  @param prm The message.
	 */
	protected IFuture handlePullResultMessage(final Object session, final PullResultMessage prm)
	{
		final Future ret = new Future();
		
//		final PullResultMessage prm = (PullResultMessage)msg;
		
//		System.out.println("Abort: "+stim);
		
		IFuture fut = incalls.get(prm.getCallid());
		if(fut instanceof IPullIntermediateFuture)
		{
			((IPullIntermediateFuture)fut).pullIntermediateResult();
//			System.out.println("Pulled intermediate result.");
		}
		else
		{
			System.out.println("Cannot pull result: "+fut+" "+prm.getCallid());
		}
		
		return ret;
	}
	
	
	
	/**
	 *  Send a result message back to the client.
	 */
	protected IFuture sendResult(Object res, String callid, Object session, boolean finished)
	{
//		if(finished && res!=null && res.getClass().toString().indexOf("ChatEvent")!=-1)
//			System.out.println("removing call: "+callid);
	
		Future ret = new Future<>();
		
		// todo: sideeffect: move out of here!
		
		if(res instanceof IService)
		{
			IService ser = (IService)res;
			jadex.bridge.ClassInfo iface = ser.getServiceId().getServiceType();
			if(!serviceinfos.containsKey(iface))
			{
				ser.getMethodInfos().then(mis -> 
				{
					serviceinfos.put(iface, mis);
					ServiceInfo si = new ServiceInfo(((IService)res).getServiceId(), getMethodNames(mis));
					sendMessage(new ResultMessage(si, callid, finished), session).delegate(ret);
				}).catchEx(ret);
			}
			else
			{
				MethodInfo[] mis = serviceinfos.get(iface);
				ServiceInfo si = new ServiceInfo(((IService)res).getServiceId(), getMethodNames(mis));
				sendMessage(new ResultMessage(si, callid, finished), session).delegate(ret);
			}
		}
		else
		{
			sendMessage(new ResultMessage(res, callid, finished), session).delegate(ret);
		}
		
		return ret;
	}
	
	/**
	 *  Get the method names.
	 *  @param mis The method infos.
	 *  @return The method names.
	 */
	protected Set getMethodNames(MethodInfo[] mis)
	{
		Set ret = new HashSet<>();
		for(MethodInfo mi: mis)
			ret.add(mi.getName());
		return ret;
	}
	
	/**
	 *  Send an exception message back to the client.
	 */
	protected IFuture sendException(Exception ex, String callid, Object session)
	{
//		System.out.println("removing call ex: "+callid);
		//if(debug) 
			ex.printStackTrace();
		return sendMessage(new ResultMessage(ex, callid), session);
	}
	
	/**
	 *  Send an message back to the client.
	 */
	protected IFuture sendMessage(BaseMessage message, Object session)
	{
		Future ret = new Future();

		try
		{
			// todo: problem classloader

			// TODO: unwrap types here instead frontend-side?

			// ensure single threaded access to socket (how to do without lock)?
			//String data = JsonTraverser.objectToString(message, this.getClass().getClassLoader(), true, null, writeprocs);
			String data = convertToString(message);

			sendWebSocketData(session, data);
			
			ret.setResult(data);
		}
		catch(Exception e)
		{
			ret.setException(e);
		}
		return ret;
	}
	
	/**
	 *  Convert an object to the json string representation.
	 *  @param val The value.
	 *  @return The string representation.
	 */
	protected String convertToString(Object val)
	{
		ISerializationServices ser = (ISerializationServices)Starter.getPlatformValue(getPlatform().getId().getRoot(), Starter.DATA_SERIALIZATIONSERVICES);
		IStringConverter conv = ser.getStringConverters().get(IStringConverter.TYPE_JSON);
		String data = conv.convertObject(val, null, this.getClass().getClassLoader(), null);
		return data;
	}
	
	/**
	 *  Convert json to object representation.
	 *  @param val The json value.
	 *  @return The object.
	 */
	protected Object convertToObject(String val, Class type)
	{
		ISerializationServices ser = (ISerializationServices)Starter.getPlatformValue(getPlatform().getId().getRoot(), Starter.DATA_SERIALIZATIONSERVICES);
		IStringConverter conv = ser.getStringConverters().get(IStringConverter.TYPE_JSON);
		Object data = conv.convertString(val, type, this.getClass().getClassLoader(), null);
		return data;
	}
	
	/**
	 *  Get or create a session component under the given key.
	 *  
	 *  // todo: create session component on other than gateway platform?!
	 */
	protected IFuture getOrCreateSessionComponent(final String key, final Object session, final String filename, final boolean create)
	{
		final Future ret = new Future();
		
		Object ma = getSessionProperties(session).get(key);
		
		if(ma instanceof IFuture)
		{
			((IFuture)ma).addResultListener(new DelegationResultListener(ret));
		}
		else if(ma==null)
		{
			if(create)
			{
				getSessionProperties(session).put(key, ret);

				getPlatform().createComponent(new CreationInfo().setFilename(filename)).addResultListener(new IResultListener()
				{
					public void resultAvailable(IExternalAccess exta)
					{
						exta.waitForTermination().addResultListener(new IResultListener>()
						{
							public void resultAvailable(java.util.Map result)
							{
								try
								{
									getSessionProperties(session).remove(key);
								}
								catch(IllegalStateException e)
								{
									// nop when session is already closed
								}
								catch(Exception e)
								{
									System.out.println("Could not remove component from session: "+key);
									e.printStackTrace();
								}
							}
							public void exceptionOccurred(Exception exception)
							{
								if(!ret.setExceptionIfUndone(exception))
								{
									System.out.println("Exception in session component");
									exception.printStackTrace();
								}
							}
						});
						ret.setResult(exta);
					}

					public void exceptionOccurred(Exception exception)
					{
						if(!ret.setExceptionIfUndone(exception))
						{
							System.out.println("Exception in session component");
							exception.printStackTrace();
						}
					}
				});
			}
			else
			{
				ret.setResult(null);
			}
		}
		
		return ret;
	}
	
	/**
	 *  Handle a partial mesage.
	 */
	protected IFuture handlePartialMessage(final Object session, final PartialMessage msg)
	{
		final Future ret = new Future();
		
		Map mypartials = partials.get(msg.getCallid());
		
		if(mypartials==null)
		{
			mypartials = new HashMap();
			partials.put(msg.getCallid(), mypartials);
		}
		
		mypartials.put(Integer.valueOf(msg.getNumber()), msg.getData());

//		System.out.println("Received: "+msg.getNumber()+" "+mypartials.size()+"/"+msg.getCount());
		
		// When all parts have arrived
		if(mypartials.size()==msg.getCount())
		{
			StringBuffer buf = new StringBuffer();
			for(int i=0; i(IComponentManagementService.class)).get();
				getPlatform().getExternalAccess(cid).killComponent();
				System.out.println("Killing session component: "+((IService)val).getServiceId().getProviderId());
			}
		}
	}
	
	/**
	 * 
	 * @param session
	 * @param txt
	 */
	public void onMessage(Object session, String txt)
	{
		System.out.println("Message received: " + txt);
//		RemoteEndpoint.Async rea = session.getAsyncRemote();
		
		// The result here ist the (last) meassage sent back
		final Future ret = new Future();
		
		try
		{
			// todo: problem classloader
			// JSonServiceProcessor: service.getServiceIdentifier().getServiceType().getType(targetcl);
			// problem getType(null) does not use default classloader but returns null :-(?!
			//final Object msg = JsonTraverser.objectFromString(txt, this.getClass().getClassLoader(), null, Object.class, readprocs);
			final Object msg = convertToObject(txt, null);
			
			if(msg instanceof PartialMessage)
			{
				handlePartialMessage(session, (PartialMessage)msg);
			}
			else if(msg instanceof ServiceSearchMessage)
			{
				handleSearchServiceMessage(session, (ServiceSearchMessage)msg).addResultListener(new DelegationResultListener(ret));
			}
			else if(msg instanceof ServiceInvocationMessage)
			{
				handleServiceInvocationMessage(session, (ServiceInvocationMessage)msg).addResultListener(new DelegationResultListener(ret));
			}
			else if(msg instanceof ServiceTerminateInvocationMessage)
			{
				handleTerminateInvocationMessage(session, (ServiceTerminateInvocationMessage)msg).addResultListener(new DelegationResultListener(ret));
			}
			else if(msg instanceof PullResultMessage)
			{
				handlePullResultMessage(session, (PullResultMessage)msg).addResultListener(new DelegationResultListener(ret));
			}
			else if(msg instanceof ServiceProvideMessage)
			{
				handleServiceProvideMessage(session, (ServiceProvideMessage)msg).addResultListener(new DelegationResultListener(ret));
			}
			else if(msg instanceof ServiceUnprovideMessage)
			{
				handleServiceUnprovideMessage(session, (ServiceUnprovideMessage)msg).addResultListener(new DelegationResultListener(ret));
			}
			else if(msg instanceof ResultMessage)
			{
				handleResultMessage(session, (ResultMessage)msg).addResultListener(new DelegationResultListener(ret));
			}
			else
			{
				System.out.println("Message type not understood: "+msg);
				ret.setResult("not understood");
			}
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
		
//		String res = fut.get();
//		session.getBasicRemote().sendText(res);
		
		ret.addResultListener(new DefaultResultListener()
		{
			public void resultAvailable(String arg0)
			{
			}
			
			@Override
			public void exceptionOccurred(Exception exception)
			{
				super.exceptionOccurred(exception);
			}
		});
	}
	
	/**
	 *  Called on close of a session
	 *  @param session The session.
	 */
	public void onClose(Object session)
	{
		cleanSessionComponents(session);
	}
	
	/**
	 *  Abstract send message method.
	 *  Must be implemented by concrete web socket impl.
	 *  @param session The web socket session.
	 *  @param data The data to send.
	 */
	public abstract void sendWebSocketData(Object session, String data);
	
	/**
	 *  Get the properties belonging to a web socket session.
	 *  @param session The session.
	 *  @return The properties.
	 */
	public abstract Map getSessionProperties(Object ws);
	
	
}