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

jadex.bridge.service.component.BasicServiceInvocationHandler Maven / Gradle / Ivy

Go to download

Jadex bridge is a base package for kernels and platforms, i.e., it is used by both and provides commonly used interfaces and classes for active components and their management.

There is a newer version: 4.0.267
Show newest version
package jadex.bridge.service.component;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;

import jadex.base.Starter;
import jadex.bridge.ClassInfo;
import jadex.bridge.IInternalAccess;
import jadex.bridge.ProxyFactory;
import jadex.bridge.ServiceCall;
import jadex.bridge.modelinfo.UnparsedExpression;
import jadex.bridge.service.BasicService;
import jadex.bridge.service.IInternalService;
import jadex.bridge.service.IRequiredServiceFetcher;
import jadex.bridge.service.IService;
import jadex.bridge.service.IServiceIdentifier;
import jadex.bridge.service.ProvidedServiceInfo;
import jadex.bridge.service.RequiredServiceBinding;
import jadex.bridge.service.RequiredServiceInfo;
import jadex.bridge.service.annotation.CheckIndex;
import jadex.bridge.service.annotation.CheckNotNull;
import jadex.bridge.service.annotation.CheckState;
import jadex.bridge.service.annotation.FutureReturnType;
import jadex.bridge.service.annotation.Raw;
import jadex.bridge.service.annotation.Service;
import jadex.bridge.service.annotation.ServiceComponent;
import jadex.bridge.service.annotation.ServiceIdentifier;
import jadex.bridge.service.component.interceptors.CallAccess;
import jadex.bridge.service.component.interceptors.DecouplingInterceptor;
import jadex.bridge.service.component.interceptors.DecouplingReturnInterceptor;
import jadex.bridge.service.component.interceptors.FutureFunctionality;
import jadex.bridge.service.component.interceptors.IntelligentProxyInterceptor;
import jadex.bridge.service.component.interceptors.MethodCallListenerInterceptor;
import jadex.bridge.service.component.interceptors.MethodInvocationInterceptor;
import jadex.bridge.service.component.interceptors.PrePostConditionInterceptor;
import jadex.bridge.service.component.interceptors.ResolveInterceptor;
import jadex.commons.SAccess;
import jadex.commons.SReflect;
import jadex.commons.SUtil;
import jadex.commons.future.ExceptionDelegationResultListener;
import jadex.commons.future.Future;
import jadex.commons.future.FutureHelper;
import jadex.commons.future.IFuture;
import jadex.javaparser.SJavaParser;

/**
 *  Basic service invocation interceptor.
 *  It has a multi collection of interceptors per method.
 *  Executes the list of interceptors one by one.
 *  In case no handler can be found a fallback handler is used.
 */
public class BasicServiceInvocationHandler implements InvocationHandler, ISwitchCall
{
	//-------- constants --------
	
	/** The raw proxy type (i.e. no proxy). */
	public static final String	PROXYTYPE_RAW	= "raw";
	
	/** The direct proxy type (supports custom interceptors, but uses caller thread). */
	public static final String	PROXYTYPE_DIRECT	= "direct";
	
	/** The (default) decoupled proxy type (decouples from caller thread to component thread). */
	public static final String	PROXYTYPE_DECOUPLED	= "decoupled";
	
	//-------- attributes --------

	/** The internal access. */
	protected IInternalAccess comp;
	
	// The proxy can be equipped with 
	// a) the IService Object
	// b) a service info object (for pojo services that separate basic service object and pojo service)
	// c) a service identifier that can be used to relay a call to another service
	
	/** The service identifier. */
	protected IServiceIdentifier sid;
	
	/** The service. */
	protected Object service;
		

	/** The logger for errors/warnings. */
	protected Logger logger;

	/** The list of interceptors. */
	protected List interceptors;
	
//	/** The root cause that was given at creation time. */
//	protected Cause cause;
	
//	/** The call id. */
//	protected AtomicLong callid;
	
	/** The flag if the proxy is required (provided otherwise). */
	protected boolean required;
	
	/** The flag if a switchcall should be done. */
	protected boolean switchcall;
	
	
	/** The pojo service map (pojo -> proxy). */
	protected static Map	pojoproxies;

	
	//-------- constructors --------
	
	/**
	 *  Create a new invocation handler.
	 */
	public BasicServiceInvocationHandler(IInternalAccess comp, IServiceIdentifier sid, Logger logger, boolean required)
	{
//		assert cause!=null;
		this.comp = comp;
		this.sid = sid;
		this.logger	= logger;
//		this.cause = cause;
		this.switchcall = true;
		this.required	= required;
//		this.callid = new AtomicLong();
	}
	
	/**
	 *  Create a new invocation handler.
	 */
	public BasicServiceInvocationHandler(IInternalAccess comp, IService service, Logger logger, boolean required)
	{
//		assert cause!=null;
		this.comp = comp;
		this.service = service;
//		this.sid = service.getId();
		this.logger	= logger;
//		this.realtime	= realtime;
//		this.cause = cause;
		this.switchcall = false; 
		this.required	= required;
//		this.callid = new AtomicLong();
	}
	
	/**
	 *  Create a new invocation handler.
	 */
	public BasicServiceInvocationHandler(IInternalAccess comp, ServiceInfo service, Logger logger)//, Cause cause)
	{
//		assert cause!=null;
		this.comp = comp;
		this.service = service;
//		this.sid = service.getManagementService().getId();
		this.logger	= logger;
//		this.realtime	= realtime;
//		this.cause = cause;
		this.switchcall = false; // called for provided proxy which must not switch (is the object that is asked in the req proxy)
//		this.callid = new AtomicLong();
	}
	
//	/**
//	 *  Create a new invocation handler.
//	 */
//	public BasicServiceInvocationHandler(IInternalAccess comp, IResultCommand, Void> searchcmd, Logger logger, Cause cause)
//	{
//		assert cause!=null;
//		this.comp = comp;
//		this.searchcmd = searchcmd;
////		this.sid = service.getManagementService().getId();
//		this.logger	= logger;
////		this.realtime	= realtime;
//		this.cause = cause;
//		this.switchcall = false; // called for provided proxy which must not switch (is the object that is asked in the req proxy)
////		this.callid = new AtomicLong();
//	}
	
	//-------- methods --------
	
	/**
	 *  A proxy method has been invoked.
	 */
	public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable
	{
		Object ret = null;
		
//		final long callid = this.callid.getAndIncrement();
//		comp.getServiceContainer().notifyMethodListeners(getServiceIdentifier(), true, proxy, method, args, callid, null);
		
//		if(method.getName().indexOf("start")!=-1 && getServiceIdentifier().getServiceType().getTypeName().indexOf("Peer")!=-1)
//			System.out.println("call method start");
//		if(method.getName().indexOf("updateClientData")!=-1 && args[0]==null)// && getServiceIdentifier().getServiceType().getTypeName().indexOf("Peer")!=-1)
//			System.out.println("call method init");
		
//		ServiceInvocationContext sicon = null;
		
		// IT IS IMPORTANT TO HANDLE getSericeId() HERE. Otherwise random bug behavior might occur
		if((args==null || args.length==0) && "getServiceId".equals(method.getName()))
		{
			ret	= getServiceIdentifier();
		}
		else if(args!=null && args.length==1 && args[0]!=null && "equals".equals(method.getName()) && Object.class.equals(method.getParameterTypes()[0]))
		{
			Object	cmp	= ProxyFactory.isProxyClass(args[0].getClass()) ? ProxyFactory.getInvocationHandler(args[0]) : args[0];
			ret	= equals(cmp);
		}
		else if(method.getAnnotation(Raw.class)!=null)
		{
			Object ser;
			if(service instanceof IInternalService)
			{
				ser = service;
			}
			else if(service instanceof ServiceInfo)
			{
				ServiceInfo si = (ServiceInfo)service;
				if(ResolveInterceptor.SERVICEMETHODS.contains(method))
				{
					ser = si.getManagementService();
				}
				else
				{
					ser = si.getDomainService();
				}
			}
			else if(ProxyFactory.isProxyClass(service.getClass()) && 
				RemoteMethodInvocationHandler.class.equals(ProxyFactory.getInvocationHandler(service).getClass()))
			{
				ser = service;
			}
			else
			{
				throw new RuntimeException("Raw service cannot be invoked on: "+service);
			}
			
			ret = method.invoke(ser, args);
		}
		else if((args==null || args.length==0) && "hashCode".equals(method.getName()))
		{
//			System.out.println("hashcode on proxy: "+getServiceIdentifier().toString());
			ret	= hashCode();
		}
		else if((args==null || args.length==0) && "toString".equals(method.getName()))
		{
//			System.out.println("hashcode on proxy: "+getServiceIdentifier().toString());
			ret	= toString();
		}
		else
		{
			final ServiceInvocationContext sic = new ServiceInvocationContext(proxy, method, getInterceptors(), 
				getServiceIdentifier().getProviderId().getRoot(), getServiceIdentifier());//, cause);
//			sicon = sic;
			
//			if(method.getName().indexOf("getExternalAccess")!=-1 && sic.getLastServiceCall()==null)
//				System.out.println("call method ex");
			
			List myargs = args!=null? SUtil.arrayToList(args): null;
			
			if(SReflect.isSupertype(IFuture.class, method.getReturnType()))
			{
				Class rettype = null;
				Annotation[][] anss = method.getParameterAnnotations();
				for(int i=0; i)t;
							else if(t instanceof ClassInfo)
								rettype = ((ClassInfo)t).getType(comp.getClassLoader());
							if(rettype!=null)
								break;
						}
					}
				}
				/*if("invokeMethod".equals(method.getName()))
				{
					ClassInfo rtype = (ClassInfo)myargs.get(3);
					if(rtype!=null)
						rettype = rtype.getType(comp.getClassLoader());
				}*/
				
				if(rettype==null)
					rettype = method.getReturnType();
				
				final Future fret = (Future)FutureFunctionality.getDelegationFuture(rettype, 
					new FutureFunctionality(logger));
//					new ServiceCallFutureFunctionality(logger, sic.getLastServiceCall(), method.getName()));
				ret	= fret;
//				System.out.println("fret: "+fret+" "+method);
//				fret.addResultListener(new IResultListener()
//				{
//					public void resultAvailable(Object result)
//					{
//						System.out.println("fret res: "+result);
//					}
//					public void exceptionOccurred(Exception exception)
//					{
//						System.out.println("fret ex: "+exception);
//					}
//				});
//				if(method.getName().indexOf("addEntry")!=-1)
//					System.out.println("connect: ");
				sic.invoke(service, method, myargs).addResultListener(new ExceptionDelegationResultListener(fret)
				{
					public void customResultAvailable(Void result)
					{
//						if(sic.getMethod().getName().indexOf("test")!=-1)
//							System.out.println("connect: "+sic.getMethod().getName());
//						if(method.getName().indexOf("start")!=-1 && getServiceIdentifier().getServiceType().getTypeName().indexOf("Peer")!=-1)
//							System.out.println("call method start end");
//						if(method.getName().indexOf("init")!=-1 && getServiceIdentifier().getServiceType().getTypeName().indexOf("Peer")!=-1)
//							System.out.println("call method init");
						try
						{
							// Although normally ret.getResult() is a future there are cases when not
							// because of mapping the method during the call (could be future method and inner one is not)
							if(sic.getResult() instanceof Exception)
							{
								fret.setException((Exception)sic.getResult());
							}
							else if(sic.getResult()!=null && !(sic.getResult() instanceof IFuture))
							{
								fret.setResult(sic.getResult());
							}
							else
							{
//								if(method.getName().equals("getRegisteredClients"))
//								{
//									System.err.println("connect getRegisteredClients future: "+fret+", "+sic.getResult());
//								}

								FutureFunctionality.connectDelegationFuture((Future)fret, (IFuture)sic.getResult());
							}
						}
						catch(Exception e)
						{
							fret.setException(e);
						}
					}
				});
			}
//			else if(method.getReturnType().equals(void.class))
//			{
//				IFuture myvoid = sic.invoke(service, method, myargs);
//				
//				// Wait for the call to return to be able to throw exceptions
//				myvoid.get();
//				ret = sic.getResult();
//				
//				// Check result and propagate exception, if any.
//				// Do not throw exception as user code should not differentiate between local and remote case.
//	//			if(myvoid.isDone())
//	//			{
//	//				myvoid.get(null);	// throws exception, if any.
//	//			}
//	//			else
//				{
//					myvoid.addResultListener(new IResultListener()
//					{
//						public void resultAvailable(Void result)
//						{
//						}
//						
//						public void exceptionOccurred(Exception exception)
//						{
//							logger.warning("Exception in void method call: "+method+" "+getServiceIdentifier()+" "+exception);
//						}
//					});
//				}
//			}
			else
			{
//				if(method.getName().indexOf("Void")!=-1)
//					System.out.println("sdfdf");
				IFuture fut = sic.invoke(service, method, myargs);
				if(fut.isDone())
				{
					//fut.get();	
					ret = sic.getResult();
				}
				else
				{
					// Try again after triggering delayed notifications.
					FutureHelper.notifyStackedListeners();
					if(fut.isDone())
					{
//						System.out.println("stacked method: "+method);
						ret = sic.getResult();
					}
					else
					{
//						logger.warning("Warning, blocking call: "+method.getName()+" "+getServiceIdentifier());
						// Waiting for the call is ok because of component suspendable
						fut.get();
						ret = sic.getResult();
					}
				}
				if(ret instanceof Throwable)
					SUtil.rethrowAsUnchecked((Throwable)ret);
			}
		}
		
//		final ServiceInvocationContext fsicon = sicon;
//		if(ret instanceof IFuture)
//		{
//			((IFuture)ret).addResultListener(new IResultListener()
//			{
//				public void resultAvailable(Object result)
//				{
//					comp.getServiceContainer().notifyMethodListeners(getServiceIdentifier(), false, proxy, method, args, callid, fsicon);
//				}
//				
//				public void exceptionOccurred(Exception exception)
//				{
//					comp.getServiceContainer().notifyMethodListeners(getServiceIdentifier(), false, proxy, method, args, callid, fsicon);
//				}
//			});
//		}
//		else
//		{
//			comp.getServiceContainer().notifyMethodListeners(getServiceIdentifier(), false, proxy, method, args, callid, fsicon);
//		}
		
		return ret;
	}
	
	/**
	 *  Get the sid.
	 *  @return the sid.
	 */
	public IServiceIdentifier getServiceIdentifier()
	{
		if(sid==null)
		{
			// Hack!!! Preserve call context after getServiceIdentifier()
			ServiceCall	sc	= CallAccess.getNextInvocation();
			CallAccess.resetNextInvocation();
			
			sid = service instanceof ServiceInfo? ((ServiceInfo)service).getManagementService().getServiceId():
				((IService)service).getServiceId();
			
			CallAccess.setNextInvocation(sc);
		}
		return sid;
	}
	
	/**
	 *  Get the service.
	 *  @return The service.
	 */
	public Object getService()
	{
		return service;
	}
	
	/**
	 *  Get the domain service.
	 *  @return The domain service.
	 */
	public Object getDomainService()
	{
		return service instanceof ServiceInfo? ((ServiceInfo)service).getDomainService(): service;
	}

	/**
	 *  Add an interceptor.
	 *  
	 *  Must be synchronized as invoke() is called from arbitrary threads.
	 */
	public synchronized void addFirstServiceInterceptor(IServiceInvocationInterceptor interceptor)
	{
		if(interceptors==null)
			interceptors = new ArrayList();
		interceptors.add(0, interceptor);
	}
	
	/**
	 *  Add an interceptor.
	 *  Must be synchronized as invoke() is called from arbitrary threads.
	 */
	public synchronized void addServiceInterceptor(IServiceInvocationInterceptor interceptor, int pos)
	{
		if(interceptors==null)
			interceptors = new ArrayList();
		// Hack? -1 for default position one before method invocation interceptor
		interceptors.add(pos>-1? pos: interceptors.size()-1, interceptor);
	}
	
	/**
	 *  Add an interceptor.
	 *  Must be synchronized as invoke() is called from arbitrary threads.
	 */
	public synchronized void addServiceInterceptor(IServiceInvocationInterceptor interceptor)
	{
		addServiceInterceptor(interceptor, -1);
	}
	
	/**
	 *  Remove an interceptor.
	 *  Must be synchronized as invoke() is called from arbitrary threads.
	 */
	public synchronized void removeServiceInterceptor(IServiceInvocationInterceptor interceptor)
	{
		if(interceptors!=null)
			interceptors.remove(interceptor);
	}
	
	/**
	 *  Get interceptors.
	 *  Must be synchronized as invoke() is called from arbitrary threads.
	 */
	public synchronized IServiceInvocationInterceptor[] getInterceptors()
	{
		return interceptors==null || interceptors.size()==0? null://new IServiceInvocationInterceptor[]{fallback}: 
			(IServiceInvocationInterceptor[])interceptors.toArray(new IServiceInvocationInterceptor[interceptors.size()]);
	}
	
	//-------- replacement methods for service proxies --------
	
	/**
	 *  Return the hash code.
	 */
	public int hashCode()
	{
		return 31+getServiceIdentifier().hashCode();
	}
	
	/**
	 *  Test if two objects are equal.
	 */
	public boolean equals(Object obj)
	{
		return obj instanceof BasicServiceInvocationHandler && ((BasicServiceInvocationHandler)obj).getServiceIdentifier().equals(getServiceIdentifier());
	}
	
	/**
	 *  Get a string representation.
	 */
	public String toString()
	{
		return getServiceIdentifier().toString();
	}
	
	//-------- static methods --------
	
	/**
	 *  Static method for creating a standard service proxy for a provided service.
	 */
	public static IInternalService createProvidedServiceProxy(IInternalAccess ia, Object service, 
		String name, Class type, IServiceInvocationInterceptor[] ics, 
		boolean monitoring, ProvidedServiceInfo info)
	{
		IServiceIdentifier sid = null;
		
		if(isProvidedServiceProxy(service))
		{
			System.out.println("Already provided service proxy: "+service);
			return (IInternalService)service;
		}
		
		IInternalService ret;
		
		if(!SReflect.isSupertype(type, service.getClass()))
			throw new RuntimeException("Service implementation '"+service.getClass().getName()+"' does not implement service interface: "+type.getName());
		
		if(service instanceof IInternalService)
		{
			sid = BasicService.createServiceIdentifier(ia, name, type, service.getClass(), ia.getModel().getResourceIdentifier(), info);
			((IInternalService)service).setServiceIdentifier(sid);
		}
			
		
//		if(type.getName().indexOf("IServiceCallService")!=-1)
//			System.out.println("hijijij");
		String proxytype	= info!=null && info.getImplementation()!=null && info.getImplementation().getProxytype()!=null
			? info.getImplementation().getProxytype() : BasicServiceInvocationHandler.PROXYTYPE_DECOUPLED;
		
		if(!PROXYTYPE_RAW.equals(proxytype) || (ics!=null && ics.length>0))
		{
			BasicServiceInvocationHandler handler = createProvidedHandler(name, ia, type, service, info);
			if(sid==null)
			{
				Object ser = handler.getService();
				if(ser instanceof ServiceInfo)
					sid = ((ServiceInfo)ser).getManagementService().getServiceId();
			}
			ret	= (IInternalService)ProxyFactory.newProxyInstance(ia.getClassLoader(), new Class[]{IInternalService.class, type}, handler);
//			try
//			{
//				((IService)service).getServiceIdentifier();
//			}
//			catch(Exception e)
//			{
//				e.printStackTrace();
//			}
			
			BasicServiceInvocationHandler.addProvidedInterceptors(handler, service, ics, ia, proxytype, monitoring, sid!=null? sid: ret.getServiceId());
//			ret	= (IInternalService)Proxy.newProxyInstance(ia.getExternalAccess()
//				.getModel().getClassLoader(), new Class[]{IInternalService.class, type}, handler);
			if(!(service instanceof IService))
			{
				if(!service.getClass().isAnnotationPresent(Service.class)
					// Hack!!! BPMN uses a proxy as service implementation.
					&& !(ProxyFactory.isProxyClass(service.getClass())
					&& ProxyFactory.getInvocationHandler(service).getClass().isAnnotationPresent(Service.class)))
				{
					//throw new RuntimeException("Pojo service must declare @Service annotation: "+service.getClass());
					ia.getLogger().warning("Pojo service should declare @Service annotation: "+service.getClass());
//					throw new RuntimeException("Pojo service must declare @Service annotation: "+service.getClass());
				}
				addPojoServiceProxy(service, ret);
			}
		}
		else
		{
			if(service instanceof IInternalService)
			{
				ret	= (IInternalService)service;
			}
			else
			{
				throw new RuntimeException("Raw services must implement IInternalService (e.g. by extending BasicService): " + service.getClass().getCanonicalName());
			}
		}
		return ret;
	}
	
	/**
	 *  Create a basic invocation handler for a provided service.
	 */
	protected static BasicServiceInvocationHandler createProvidedHandler(String name, IInternalAccess ia, Class type, Object service, ProvidedServiceInfo info)
	{
//		if(type.getName().indexOf("ITestService")!=-1 && ia.getComponentIdentifier().getName().startsWith("Global"))
//			System.out.println("gaga");
		
		Map serprops = new HashMap();
		if(info != null && info.getProperties() != null)
		{
			for(UnparsedExpression exp : info.getProperties())
			{
				Object val = SJavaParser.parseExpression(exp, ia.getModel().getAllImports(), ia.getClassLoader()).getValue(null);
				serprops.put(exp.getName(), val);
			}
		}
		
		BasicServiceInvocationHandler handler;
		if(service instanceof IService)
		{
			IService ser = (IService)service;
			
			if(service instanceof BasicService)
			{
				serprops.putAll(((BasicService)service).getPropertyMap());
				((BasicService)service).setPropertyMap(serprops);
			}
			
			handler = new BasicServiceInvocationHandler(ia, ser, ia.getLogger(), false);
			
//			if(type==null)
//			{
//				type = ser.getServiceIdentifier().getServiceType();
//			}
//			else if(!type.equals(ser.getServiceIdentifier().getServiceType()))
//			{
//				throw new RuntimeException("Service does not match its type: "+type+", "+ser.getServiceIdentifier().getServiceType());
//			}
		}
		else
		{
			if(type==null)
			{
				// Try to find service interface via annotation
				if(service.getClass().isAnnotationPresent(Service.class))
				{
					Service si = (Service)service.getClass().getAnnotation(Service.class);
					if(!si.value().equals(Object.class))
					{
						type = si.value();
					}
				}
				// Otherwise take interface if there is only one
				else
				{
					Class[] types = service.getClass().getInterfaces();
					if(types.length!=1)
						throw new RuntimeException("Unknown service interface: "+SUtil.arrayToString(types));
					type = types[0];
				}
			}
			
			Class serclass = service.getClass();

			BasicService mgmntservice = new BasicService(ia.getId(), type, serclass, null);
			mgmntservice.setServiceIdentifier(BasicService.createServiceIdentifier(ia, name, type, service.getClass(), ia.getModel().getResourceIdentifier(), info));
			serprops.putAll(mgmntservice.getPropertyMap());
			mgmntservice.setPropertyMap(serprops);
			
			// Do not try to call isAnnotationPresent for Proxy on Android
			// see http://code.google.com/p/android/issues/detail?id=24846
			if(!(ProxyFactory.isProxyClass(serclass)))
			//if(!(SReflect.isAndroid() && ProxyFactory.isProxyClass(serclass)))
			{
				while(!Object.class.equals(serclass))
				{
					Field[] fields = serclass.getDeclaredFields();
					for(int i=0; i clazz = sid.getServiceType().getType(ia.getClassLoader());
				boolean addhandler = false;
				Method[] ms = SReflect.getAllMethods(clazz);
				
				formethod:
				for (Method m : ms)
				{
					Annotation[] as = m.getAnnotations();
					for (Annotation anno : as)
						if (anno instanceof CheckNotNull 
							|| anno instanceof CheckState
							|| anno instanceof CheckIndex)
						{
							addhandler = true;
							break formethod;
						}
				}
				if (addhandler)
					handler.addFirstServiceInterceptor(new PrePostConditionInterceptor(ia));
			}
			catch (Exception e)
			{
			}
			
			if(!(service instanceof IService))
				handler.addFirstServiceInterceptor(new ResolveInterceptor(ia));
			
			handler.addFirstServiceInterceptor(new MethodCallListenerInterceptor(ia, sid));
//			handler.addFirstServiceInterceptor(new ValidationInterceptor(ia));
			if(!PROXYTYPE_DIRECT.equals(proxytype))
				handler.addFirstServiceInterceptor(new DecouplingInterceptor(ia, Starter.isParameterCopy(sid.getProviderId()), false));
			handler.addFirstServiceInterceptor(new DecouplingReturnInterceptor());
			
			// used only by global service pool, todo add contionally
			handler.addFirstServiceInterceptor(new IntelligentProxyInterceptor(ia.getExternalAccess(), sid));
		}
		
		if(ics!=null)
		{
			for(int i=0; i0)
			{
				for(int i=0; i> ifaces = new HashSet<>();
			Class iface = sid.getServiceType().getType(cl);
			if(iface!=null)
				ifaces.add(iface);
			for(ClassInfo ci: sid.getServiceSuperTypes())
			{
				iface = ci.getType(cl);
				if(iface!=null)
					ifaces.add(iface);
			}
			
			ifaces.add(IService.class);
			
			ret = (IService)ProxyFactory.newProxyInstance(ia.getClassLoader(), ifaces.toArray(new Class[ifaces.size()]), handler); 	
			
			// todo: think about orders of decouping interceptors
			// if we want the decoupling return interceptor to schedule back on an external caller actual order must be reversed
			// now it can only schedule back on the hosting component of the required proxy
		}
		
		return ret;
	}
	
	/**
	 *  Add a service proxy.
	 *  @param pojo The pojo.
	 *  @param proxy The proxy.
	 */
	public static void addPojoServiceProxy(Object pojo, IService proxy)
	{
//		System.out.println("add pojoproxy: "+proxy.getServiceIdentifier());
		
		synchronized(BasicServiceInvocationHandler.class)
		{
			if(pojoproxies==null)
				pojoproxies = new IdentityHashMap();
			pojoproxies.put(pojo, proxy);
		}
	}
	
	/**
	 *  Remove a pojo - proxy pair.
	 *  @param sid The service identifier.
	 */
	public static void removePojoServiceProxy(IServiceIdentifier sid)
	{
		synchronized(BasicServiceInvocationHandler.class)
		{
			for(Iterator it=pojoproxies.values().iterator(); it.hasNext(); )
			{
				IService proxy = it.next();
				
				if(sid.equals(proxy.getServiceId()))
				{
					it.remove();
					break;
//					System.out.println("rem: "+pojosids.size());	
				}
			}
		}
	}
	
	/**
	 *  Get the proxy of a pojo service.
	 *  @param pojo The pojo service.
	 *  @return The proxy of the service.
	 */
	public static IService getPojoServiceProxy(Object pojo)
	{
		synchronized(BasicServiceInvocationHandler.class)
		{
			return pojoproxies.get(pojo);
		}
	}
	
	/**
	 *  Check if a switch call should be done.
	 *  @return True, if switch should be done.
	 */
	public boolean isSwitchCall()
	{
		return switchcall;
	}
	
	/**
	 *  Check if the handler is for a required service proxy.
	 */
	public boolean isRequired()
	{
		return required;
	}
	
//	/**
//	 *  Add a method listener.
//	 */
//	public void addMethodListener(MethodInfo m, IMethodInvocationListener listener)
//	{
//		if(methodlisteners==null)
//			methodlisteners = new HashMap>();
//		List lis = methodlisteners.get(m);
//		if(lis==null)
//		{
//			lis = new ArrayList();
//			methodlisteners.put(m, lis);
//		}
//		lis.add(listener);
//	}
//	
//	/**
//	 *  Add a method listener.
//	 */
//	public void removeMethodListener(MethodInfo m, IMethodInvocationListener listener)
//	{
//		if(methodlisteners!=null)
//		{
//			List lis = methodlisteners.get(m);
//			if(lis!=null)
//			{
//				lis.remove(listener);
//			}
//		}
//	}
//	
//	/**
//	 *  Notify registered listeners in case a method is called.
//	 */
//	protected void notifyMethodListeners(boolean start, Object proxy, final Method method, final Object[] args, long callid)
//	{
//		if(methodlisteners!=null)
//		{
//			doNotifyListeners(start, proxy, method, args, callid, methodlisteners.get(null));
//			doNotifyListeners(start, proxy, method, args, callid, methodlisteners.get((new MethodInfo(method))));
//		}
//	}
//	
//	/**
//	 *  Do notify the listeners.
//	 */
//	protected void doNotifyListeners(boolean start, Object proxy, final Method method, final Object[] args, long callid, List lis)
//	{
//		if(lis!=null)
//		{
//			for(IMethodInvocationListener ml: lis)
//			{
//				if(start)
//				{
//					ml.methodCallStarted(proxy, method, args, callid);
//				}
//				else
//				{
//					ml.methodCallFinished(proxy, method, args, callid);
//				}
//			}
//		}
//	}
	
//	/**
//	 * 
//	 */
//	public static void addPojoServiceIdentifier(Object pojo, IServiceIdentifier sid)
//	{
//		if(pojosids==null)
//		{
//			synchronized(BasicServiceInvocationHandler.class)
//			{
//				if(pojosids==null)
//				{
//					pojosids = Collections.synchronizedMap(new HashMap());
//				}
//			}
//		}
//		pojosids.put(pojo, sid);
////		System.out.println("add: "+pojosids.size());
//	}
//	
//	/**
//	 * 
//	 */
//	public static void removePojoServiceIdentifier(IServiceIdentifier sid)
//	{
//		if(pojosids!=null)
//		{
//			pojosids.values().remove(sid);
////			System.out.println("rem: "+pojosids.size());
//		}
//	}
//	
//	/**
//	 * 
//	 */
//	public static IServiceIdentifier getPojoServiceIdentifier(Object pojo)
//	{
//		return (IServiceIdentifier)pojosids.get(pojo);
//	}
	
	/**
	 *  Test if a service is a required service proxy.
	 *  @param service The service.
	 *  @return True, if is required service proxy.
	 */
	public static boolean isRequiredServiceProxy(Object service)
	{
		boolean ret = false;
		if(ProxyFactory.isProxyClass(service.getClass()))
		{
			Object tmp = ProxyFactory.getInvocationHandler(service);
			if(tmp instanceof BasicServiceInvocationHandler)
			{
				BasicServiceInvocationHandler handler = (BasicServiceInvocationHandler)tmp;
				ret = handler.isRequired();
			}
		}
		return ret;
	}
	
	/**
	 *  Test if a service is a provided service proxy.
	 *  @param service The service.
	 *  @return True, if is provided service proxy.
	 */
	public static boolean isProvidedServiceProxy(Object service)
	{
		boolean ret = false;
		if(ProxyFactory.isProxyClass(service.getClass()))
		{
			Object tmp = ProxyFactory.getInvocationHandler(service);
			if(tmp instanceof BasicServiceInvocationHandler)
			{
				BasicServiceInvocationHandler handler = (BasicServiceInvocationHandler)tmp;
				ret = !handler.isRequired();
			}
		}
		return ret;
	}
}