/*AXISAdapterImpl.java wrriten by JIa Yu at DSTC,Austrila*/

package axis.adapter;

import com.sun.jini.madison.Adapter;
import com.sun.jini.madison.AdapterContext;
import com.sun.jini.madison.SurrogateID;
import com.sun.jini.madison.RegistrationDisabledException;
import com.sun.jini.madison.util.Tasks;
import com.sun.jini.madison.util.HostResources;
import com.sun.jini.thread.WakeupManager;
import java.io.InputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.security.Permission;
import java.security.PrivilegedAction;
import java.security.AccessController;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import net.jini.discovery.DiscoveryPermission;
import net.jini.surrogate.SurrogateCreationException;
import net.jini.surrogate.KeepAliveHandler;
import axis.adapter.AXISInterconnectContext;

public class AXISAdapterImpl implements Adapter{
	
	private static final int INSTANTIATED=0;
	private static final int ACTIVATED=1;
	private static final int DEACTIVATED=2;

	private HashMap interconnectContexts;	
	private boolean registrationEnabled;
	private AdapterContext adapterContext;
	private int state;
	private MonitorAXISTask mirAXISTask;

	private long curKeepAlivePeriod=1000L*60;


	public AXISAdapterImpl() throws IOException{
		System.err.println("start axis adapter");
		
		interconnectContexts=new HashMap();
		registrationEnabled=false;
		state=INSTANTIATED;	
	}
	//--------------------------------------------
	//	public methods
	//--------------------------------------------
	public synchronized void activate(AdapterContext adapterContext)
	{
		this.activate(adapterContext,true);
	}
	public synchronized void activate(AdapterContext adapterContext,boolean enableRegistration)
	{
		if(adapterContext==null)
			throw new IllegalArgumentException("adapterContext is null");
		if(state==ACTIVATED)
			throw new IllegalStateException("adapter activated already");
		else if(state>=DEACTIVATED)	
			throw new IllegalStateException("adapter deactivated already");
		this.adapterContext=adapterContext;
		registrationEnabled=enableRegistration;

		mirAXISTask=new MonitorAXISTask();
		HostResources.getInstance().addTask(mirAXISTask);
		state=ACTIVATED;
		System.out.println("activate AXIS adapter");
	}
	public synchronized void deactivate(){
		if(state>=DEACTIVATED)
			return;  //only need to be deactivated once

		registrationEnabled=false; //turn registration off
		mirAXISTask.cancel();

		//terminate intercontext
		for(Iterator iter=interconnectContexts.values().iterator();iter.hasNext();){
			((AXISInterconnectContextImpl)iter.next()).terminate();
		}
		interconnectContexts.clear();
				
		state=DEACTIVATED;
		notifyAll();
		System.out.println("adapter is deactivated");
	}
	public synchronized void setRegistrationEnabled(boolean flag){
		if(state!=ACTIVATED)
			return;
		if(registrationEnabled!=flag){
			registrationEnabled=flag;
			if(registrationEnabled)
				notifyAll();
		}
	}
	public synchronized boolean isRegistrationEnabled(){
		return registrationEnabled;
	}
	public synchronized void surrogateDeactivated(SurrogateID id){
		System.err.println("surrogateDeactivated");	
	}
	
	//----------------------------------
	//	private methods
	//----------------------------------
	private Permission[] additionalPermissions(ArrayList perms){
		perms.add(new DiscoveryPermission("*"));
		return (Permission[])perms.toArray(new Permission[perms.size()]);
	}
	private class MonitorAXISTask extends Tasks.IndependentTask 
	{
		private boolean canceled;
		private boolean axisOn;
		
		MonitorAXISTask(){
			canceled=false;
			axisOn=false;
		}
		
		public void cancel(){
		
			synchronized(this){
				if(canceled)
					return;
				canceled=true;
			}
		}

		public void run(){
			synchronized(this){
				
				if(canceled){
					return; //canceled already, do nothing
				}
			}
			if(!isRegistrationEnabled()){
				System.out.println("Surrogate is on");// there is only one axis
			}else{
				
				axisOn=true;
				Socket testAXIS=null;
				try{
				   String axisHost=System.getProperty("axis.hostIP");
				   testAXIS=new Socket(axisHost,80);
				}catch(UnknownHostException e){
					axisOn=false;	
					e.printStackTrace();
				}catch(IOException e){
					axisOn=false;
					e.printStackTrace();
				}
			
				if(axisOn){
					 System.out.println("axis is on");
				 	 
					 File jarFile=new File("/jini_surr_madison1_0/lib/axis_surrogate.jar");
					 FileInputStream fis=null;
					 try{
						fis=new FileInputStream(jarFile);
					 }catch(FileNotFoundException e){
						System.err.println("File not found");
					 }
					 DataInputStream dis=new DataInputStream(fis);
					 SurrogateID id=null;
					 try{
						AXISInterconnectContextImpl a=new AXISInterconnectContextImpl();
						ArrayList perms=new ArrayList();
					
						id=adapterContext.registerSurrogate((InputStream)dis,(int)jarFile.length(),a,additionalPermissions(perms));

						a.setSurrogateID(id);
						synchronized(AXISAdapterImpl.this){
							if(AXISAdapterImpl.this.state==ACTIVATED){
								interconnectContexts.put(id,a);
							}
							else{
								adapterContext.deactivateSurrogate(id);
								a.terminate();
							}
						}
					}catch(RegistrationDisabledException e){
						System.out.println("registrationDisabledException");
					}catch(SurrogateCreationException e){
						System.out.println("SurrogateCreationExceptione");
					}	
					System.out.println("registered");	
					 try{
						testAXIS.close();
					 }catch(IOException e){}

				}
				else System.out.println("axis is off");
			}

			long nextMirTime=System.currentTimeMillis()+10000;
			HostResources.getInstance().scheduleTask(nextMirTime,new Runnable(){
				public void run(){
					HostResources.getInstance().addTask(MonitorAXISTask.this);
				}
			});
		}
		
	}

	private class AXISInterconnectContextImpl implements AXISInterconnectContext
	{
		private SurrogateID id;
		private boolean contextTerminated;
		private KeepAliveTask keepAliveTask;
		
		AXISInterconnectContextImpl(){
			contextTerminated=false;
		}
		public void setSurrogateID(SurrogateID id){
			this.id=id;
		}
		
		public synchronized void setKeepAliveHandler(final KeepAliveHandler handler){
			if(contextTerminated){
				throw new IllegalStateException("Sorry, the surrogate's"+
								"interconnect context has"+
								"been terminated.");
			}
			
			AccessController.doPrivileged(new PrivilegedAction(){
				public Object run(){
					System.err.println("keepAlive action");
					if(contextTerminated)
						return null;
					if(keepAliveTask!=null){
						keepAliveTask.terminate();
						keepAliveTask=null;
						System.err.println("keepAliveTask in action terminate");
						HostResources.getInstance().removeTask(keepAliveTask);
					}
					long now=System.currentTimeMillis();
					if(handler!=null){
						keepAliveTask=new KeepAliveTask(id,handler);
						HostResources.getInstance().addTask(keepAliveTask);
					}
					return null;
				}
			});
			
		}
		public synchronized void terminate(){
			if(contextTerminated)
				return; 
			System.err.println("intercontext is terminated");
			if(keepAliveTask!=null){
				System.err.println("terminate adapter");
				keepAliveTask.terminate();
				HostResources.getInstance().removeTask(keepAliveTask);
				keepAliveTask=null;
				HostResources.getInstance().addTask(new Tasks.IndependentTask(){
					public void run(){
						adapterContext.deactivateSurrogate(id);
					}
				});
			}
			contextTerminated=true;
		}
	}

	private class KeepAliveTask extends Tasks.IndependentTask{
		private SurrogateID id;
		private KeepAliveHandler handler;
		private long callStartTime;
		private long returnedCallCount;
		private WakeupManager.Ticket checkTaskTicket;
		private boolean taskTerminated;
		
		KeepAliveTask(SurrogateID id,KeepAliveHandler handler){
			this.id=id;
			this.handler=handler;
			returnedCallCount=0;
			taskTerminated=false;
		}
		public synchronized void terminate(){
			if(taskTerminated)
				return; //only need to terminate once
			taskTerminated=true;
		}
		public void run(){
			if(taskTerminated)
				return;
			callStartTime=System.currentTimeMillis();
			
			//schedule the return on time checking task
			long period=curKeepAlivePeriod;
			final long oldReturnedCallCount=returnedCallCount;
			Runnable task=new Runnable(){
				public void run(){
					HostResources.getInstance().addTask(new Tasks.IndependentTask(){
						public void run(){
							if(!taskTerminated){
								check();
							}
						}
					});
				}
				private void check(){
					if(returnedCallCount<=oldReturnedCallCount){
						KeepAliveTask.this.terminate();
						HostResources.getInstance().removeTask(KeepAliveTask.this);
						Exception e=new Exception("The keepAlive method of "+
									id+"did not return on time.");
						KeepAliveTask.this.cleanup(e);
					}
				}
			};
			checkTaskTicket=HostResources.getInstance().scheduleTask(System.currentTimeMillis()+period,task);
			try{
				handler.keepAlive(period);
				returnedCallCount++;
				period-=System.currentTimeMillis()-callStartTime;
				if(Thread.interrupted())
					return; // the return checking task interrupted us
				HostResources.getInstance().cancelScheduledTask(checkTaskTicket);
				if(period<=0){
					throw new Exception("The keepAlive method of "+id+"did not return on time.");
				}
			}catch(Throwable t){
				cleanup(t);
				return;
			}
			period=Math.max(period/2,period-1000*10);
			if(!taskTerminated){
				long nextCallTime=System.currentTimeMillis()+period;
				HostResources.getInstance().scheduleTask(nextCallTime,new Runnable(){
					public void run(){
						HostResources.getInstance().addTask(KeepAliveTask.this);
					}
				});
			}
		}
		private void cleanup(Throwable t){
			if(System.getProperty("com.sun.jini.madison.debug")!=null)
				t.printStackTrace();
			adapterContext.deactivateSurrogate(id);
			AXISInterconnectContextImpl axisc=null;
			synchronized(AXISAdapterImpl.this){
				axisc=(AXISInterconnectContextImpl) interconnectContexts.remove(id);
			}
			if(axisc!=null)
				axisc.terminate();
			return;
		}
	}		

				
}			 
			
		
	