/*
 * Created on 05-04-2005
 * Copyright (c) 2005 Fujitsu Laboratories of Europe. All rights reserved.
 */

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

import javax.xml.namespace.QName;

import org.apache.axis.message.MessageElement;
import org.apache.axis.message.addressing.EndpointReferenceType;
import org.oasis.wsrf.bf.BaseFaultType;
import org.oasis.wsrf.rp.GetResourcePropertyResponse;
import org.oasis.wsrf.rp.InvalidResourcePropertyQNameFaultType;
import org.oasis.wsrf.rp.ResourceUnknownFaultType;
import org.unicore.services.wsrf.factory.CreateWSRF;
import org.unicore.services.wsrf.factory.CreateWSRFResponse;
import org.unicore.services.wsrf.factory.WSRFFactory;
import org.unicore.services.wsrf.factory.WSRFFactoryService;
import org.unicore.services.wsrf.factory.WSRFFactoryServiceLocator;
import org.unicore.services.wsrf.job.Job;
import org.unicore.services.wsrf.job.JobServiceAddressingLocator;
import org.unicore.services.wsrf.job.JobSoapBindingStub;
import org.unicore.services.wsrf.lt.ResourceLifetimePortType;
import org.unicore.services.wsrf.lt.ResourceLifetimeServiceAddressingLocator;
import org.unicore.services.wsrf.lt.ResourceLifetimeSoapBindingStub;
import org.unicore.services.wsrf.rp.ResourcePropertiesPortType;

import com.fujitsu.unicore.services.util.JobStatusType;

/**
 * @author Sven van den Berghe, 	Fujitsu Laboratories of Europe
 * @author Vivian Li, 			Fujitsu Laboratories of Europe
 * @author Dave Snelling,			Fujitsu Laboratories of Europe
 * 
 * @version $$Id: UnicoreWSRF_Client.java,v 1.12 2005/06/13 12:33:57 svandenberghe Exp $$
 * 
 * A web service resources framework client.
 * <p>
 * This implementation contains Unicore operations of storage management, job management, and file transfer.
 * All the wsrf operations are accessible by "extends" this class to WSRF_Client.
 */
public class UnicoreWSRF_Client extends WSRF_Client {

	//adding namespace and its prefix so that the command line just need to take prefix of rps.
	{
		RPNameConverter.addPrefixNames("unicore","http://unicore.org/services/types");
		RPNameConverter.addPrefixNames("jsdl","http://schemas.ggf.org/jsdl/2005/06/jsdl");
		
		RPNameConverter.addQnameNames("JobStatus", new QName("http://unicore.org/services/types","JobStatusType"));
		RPNameConverter.addQnameNames("Status", new QName("http://unicore.org/services/types","StatusType"));
		RPNameConverter.addQnameNames("JobReference", new QName("http://schemas.xmlsoap.org/ws/2004/08/addressing","EndpointReferenceType"));
	}
	
	private static UnicoreWSRF_Client client;
	private static Job job;
	
	//main method mainly taking the command line arguments to perform the unicore as well as wsrf operations.
	public static void main(String[] args) throws Exception {

		if (args.length != 1) {
			System.err.println(
				"Needs an endpoint for the WSRF factory (-Dtcp for via tcpmon)");
			System.exit(1);
		}
		String factory_endpoint = args[0];

		client = new UnicoreWSRF_Client(new URL(factory_endpoint));
		
		
		
		// This creates an instance of the "factory" for Jobs - in the sense that inserting
		// a JSDL document as a Resource Property creates a Job on the Unicore/GS ssyetm. This can
		// be manipulated through the "Job" type.
		ResourceLifetimePortType wsrf = client.create(new URL(factory_endpoint));
		
		// Set up input from stdin, first the accepted operations:
		
		client.addOperation("man", client.new Manual());
		client.addOperation("quit",client.new Quit());
		client.addOperation("destroy",client.new Destroy());
		client.addOperation("settt",client.new SetTerminationTime());
		client.addOperation("insertrp",client.new Insert());
		client.addOperation("updaterp",client.new Update());
		client.addOperation("deleterp",client.new Delete());
		client.addOperation("grp",client.new Get());
		client.addOperation("gmrp",client.new GetMultiple());
		client.addOperation("grpd",client.new GetDocument());
		client.addOperation("gjob",client.new GetJob());
		
		// Loop over input from stdin
		BufferedReader inp =new BufferedReader(new InputStreamReader(System.in));
		while (true) {
		    
		    System.out.print("\nwsrf services> ");
			String line;
			try {
				line = inp.readLine();
				if (line == null) {
					break;
				}
				line = line.trim();
			} catch (java.io.IOException ioex) {
				break;
			}

			if (line.length() == 0) continue;

			StringTokenizer st = new StringTokenizer(line);
			String command = st.nextToken().toLowerCase();
			
			Operation op = (Operation) operations.get(command);
			if(op == null) {
			    System.out.println("-ERROR: Unrecognised command <"+command+">");
			}
			else {
			    op.doOperation(st);
			}
		}
	}

	private URL factory_endpoint;
	public UnicoreWSRF_Client(URL factory_endpoint) {
		this.factory_endpoint = factory_endpoint;
	}
	
	//create a target system service intance from the target system factory
	private ResourceLifetimePortType wsrf;
	
	
	private ResourceLifetimePortType create(URL factory_endpoint) {

		System.out.println("Creating a WSRF... (man command for operation mannul)\n");

		try {
			//creating a target system factory from the given published url.
			WSRFFactoryService service = new WSRFFactoryServiceLocator();
			WSRFFactory factory =
				service.getWSRFFactoryService(factory_endpoint);

			CreateWSRFResponse	create	= factory.createWSRF(new CreateWSRF());
			EndpointReferenceType epr 	= create.getWsrfReference();

			System.out.println("EPR:Address	= " + epr.getAddress());
			System.out.println("EPR:Ref Prop ResourceId	= "+ (epr.getProperties() == null? "null props": epr.getProperties().get_any()[0].getAsString()));
			
			ResourceLifetimeServiceAddressingLocator wsrf_service = new ResourceLifetimeServiceAddressingLocator();
			wsrf = wsrf_service.getResourceLifetimePortType(epr);
			((ResourceLifetimeSoapBindingStub)wsrf)._setProperty(com.fujitsu.unicore.services.impl.wsa.WSAddressingHeaders.REFERENCE_PROPERTY_NAME, epr.getProperties().get_any()[0]);
			
		} catch (BaseFaultType ex) {
			System.out.println("BaseFault: "+ ex.getMessage()+ ":"+ ex.getTimestamp().getTime());
			ex.printStackTrace();
		} catch (Exception ex) {
			ex.printStackTrace();
		}
		
		return wsrf;
	}


abstract class Operation {
    public abstract String getManLine();
    public abstract void doOperation(StringTokenizer st);
}

private static Map operations = new HashMap();

private void addOperation(String tag, Operation op) {
    operations.put(tag,op);
}

class Manual extends Operation {
    
    public String getManLine() {return "return the available operations and arguments:\n"+
    		"Every operation must be followed by either \"wsrf\" or \"job\"\n"+
    		"where job refers to the most recent job from a gjob() method\n" +
    		"most operations also have more arguments\n";}
    
    public void doOperation(StringTokenizer st) {
        System.out.println("");
        Iterator i = operations.keySet().iterator();
        while(i.hasNext()) {
            String tag = (String)i.next();
            System.out.println("<"+tag+"> "+((Operation)operations.get(tag)).getManLine());
        } 
    }
}

class Quit extends Operation {
    
    public String getManLine() {return "quit application (will lose handle on Jobs and factory)\n";}
    
    public void doOperation(StringTokenizer st) {
        System.out.println("OK");
        System.exit(0);
    }
}

class Destroy extends Operation {
    
    public String getManLine() {return "destroy a resource";}
    
    public void doOperation(StringTokenizer st) {
		try {
			if (st.hasMoreTokens()) {
				String service_name = st.nextToken();
				
				if(service_name.equals("wsrf"))
				    client.destroy(wsrf);
				else if(service_name.equals("job")){
				    if(job!=null)
				        client.destroy(job);
				    else
				        System.out.println("-ERROR: please submit a job first");
				}else
				    System.out.println("-ERROR: please provide either wsrf or job to destroy");
			} else {
				System.out.println("- ERROR: please provide a service instance");
			}
		} catch (Exception ex) {
			ex.printStackTrace();
		}
    }
}

class SetTerminationTime extends Operation {
    
    public String getManLine() {return "adjust termination time: extra arguement is new termination time offset from current time in minutes\n";}
    
    public void doOperation(StringTokenizer st) {
		try {
			if (st.countTokens() != 2)
				System.out.println("- ERROR: insufficient arguments for this command");
			else{
			    String service_name=st.nextToken();
			    
			    if(service_name.equalsIgnoreCase("wsrf"))
			       client.settt(wsrf,Integer.parseInt(st.nextToken()));//time in minutes
			    else if(service_name.equals("job")){
			        if(job!=null)
				        client.settt(job,Integer.parseInt(st.nextToken()));
				    else
				        System.out.println("-ERROR: please submit a job first");    
			    }else
				    System.out.println("-ERROR: please provide either wsrf or job to adjust time");
			}
		} catch (Exception ex) {
			ex.printStackTrace();
		}
    }
}

class Insert extends Operation {
    
    public String getManLine() {return "insert resource properties:\nextra arguements are pairs:\n" +
    		"first value of pair is resource property qname\n"+
    		"second value of pair is resource property value\n";}
    
    public void doOperation(StringTokenizer st) {
		try {
			if (st.countTokens() < 3)
				System.out.println("- ERROR: insufficient arguments for this command");
			else{
			    
			    String service_name=st.nextToken();
			    
			    List values = new ArrayList();
				
				while(st.hasMoreTokens()){
				    String rp = st.nextToken();
				    values.add(rp);
				    if (rp.startsWith("jsdl")){
				
				        File jsdl_doc = new File (st.nextToken());
				        long length = jsdl_doc.length();
				        
				        byte[] jsdl_bytes = new byte[(int) length];
				        
				        FileInputStream in = new FileInputStream(jsdl_doc);
				        in.read(jsdl_bytes);
				   
				        String jsdl = new String(jsdl_bytes);
						values.add(jsdl);
				    }   
				}
				
				if(service_name.equalsIgnoreCase("wsrf"))
				    client.insertrp(wsrf, values);
			    else{
			        if(job!=null)
			            client.insertrp(job, values);
				    else
				        System.out.println("-ERROR: please submit a job first");    
			    }
				
			}
		} catch (Exception ex) {
			ex.printStackTrace();
		}
    }
}

class Update extends Operation {
    
    public String getManLine() {return "update an existing resource:\nextra arguements are pairs:\n" +
    		"first value of pair is resource property qname\n"+
    		"second value of pair is resource property value\n";}
    
    public void doOperation(StringTokenizer st) {
		try {
			if (st.countTokens() < 3)
				System.out.println("- ERROR: insufficient arguments for this command");
			else{
			    
			    String service_name=st.nextToken();
			    
				List values = new ArrayList();
				
				while(st.hasMoreTokens()){
					values.add(st.nextToken());
				}
				
				if(service_name.equalsIgnoreCase("wsrf"))
				    client.updaterp(wsrf, values);
				else if(service_name.equals("job")){
			        if(job!=null)
			            client.updaterp(job, values);
				    else
				        System.out.println("-ERROR: please submit a job first");    
			    }else
				    System.out.println("-ERROR: please provide either wsrf or job to adjust time");
			}
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	
    }
}

class Delete extends Operation {
    
    public String getManLine() {return "delete resource properties: extra arguements are resource property qnames\n";}
    
    public void doOperation(StringTokenizer st) {
		try {
			if (st.countTokens() < 2 )
				System.out.println("- ERROR: insufficient arguments for this command");
			else{
			    
			    String service_name=st.nextToken();
			    
				String prop_name = st.nextToken();
				
				if(service_name.equalsIgnoreCase("wsrf"))
				    client.deleterp(wsrf,prop_name);
				else if(service_name.equals("job")){
			        if(job!=null)
			            client.deleterp(job,prop_name);
				    else
				        System.out.println("-ERROR: please submit a job first");    
			    }else
				    System.out.println("-ERROR: please provide either wsrf or job to delete resource property");
			}
		} catch (Exception ex) {
			ex.printStackTrace();
		}	
	}
}

class Get extends Operation {
    
    public String getManLine() {return "get a resource property value:\nextra arguement is resource property qname\n";}
    
    public void doOperation(StringTokenizer st) {
		try {
			if (st.countTokens() != 2)
				System.out.println("- ERROR: insufficient arguments for this command");
			else{
			    
			    String service_name=st.nextToken();
				String prop_name = st.nextToken();
				
				if(service_name.equalsIgnoreCase("wsrf"))
					client.grp(wsrf,prop_name);
				else if(service_name.equals("job")){
			        if(job!=null)
						client.grp(job,prop_name);
				    else
				        System.out.println("-ERROR: please submit a job first");    
			    }else
				    System.out.println("-ERROR: please provide either wsrf or job to get a resource property");
			}
		} catch (Exception ex) {
			ex.printStackTrace();
		}
    }
}

class GetMultiple extends Operation {
    
    public String getManLine() {return "get multiple resource property values:\nextra arguements are pairs:\n" +
    		"first value of pair is resource property qname\n"+
    		"second value of pair is resource property value\n";}
    
    public void doOperation(StringTokenizer st) {
		try {
			if (st.countTokens() < 2)
				System.out.println("- ERROR: insufficient arguments for this command");
			else{
			    
			    String service_name= st.nextToken();
			    
				List names = new ArrayList();
				while (st.hasMoreTokens())
					names.add(st.nextToken());
				
				if(service_name.equalsIgnoreCase("wsrf"))
				    client.gmrp(wsrf, (String [])names.toArray(new String[names.size()]));
				else if(service_name.equals("job")){
			        if(job!=null)
			            client.gmrp(job,(String [])names.toArray(new String[names.size()]));
				    else
				        System.out.println("-ERROR: please submit a job first");    
			    }else
				    System.out.println("-ERROR: please provide either wsrf or job to get multiple resource properties");
			}
		} catch (Exception ex) {
			ex.printStackTrace();
		}
    }
}

class GetDocument extends Operation {
    
    public String getManLine() {return "get resource property document\n";}
    
    public void doOperation(StringTokenizer st) {
		try{
		    if(st.countTokens()!=1)
		        System.out.println("-ERROR: insufficient arguments for this command");
		    
		    String service_name=st.nextToken();
		    
		    if(service_name.equalsIgnoreCase("wsrf"))
		        client.grpd(wsrf);
		    else if(service_name.equals("job")){
		        if(job!=null)
		            client.grpd(job);
			    else
			        System.out.println("-ERROR: please submit a job first");    
		    }else
			    System.out.println("-ERROR: please provide either wsrf or job to get multiple resource properties");
		    
		}catch (Exception ex){
			ex.printStackTrace();
		}
	
    }
}

class GetJob extends Operation {
    
    public String getManLine() {return "return a job: extra arguement is job name\n";}
    
    public void doOperation(StringTokenizer st) {
		try {
			if (st.countTokens() < 1)
				System.out.println("- ERROR: insufficient arguments for this command");
			else{
				String jobName=st.nextToken();
			    job = getJob(jobName, "unicore:JobStatus", wsrf);
			}
		} catch (Exception ex) {
			ex.printStackTrace();
		}
    }
  
	private Job getJob(String jobName, String rp, Object service){
	    
		QName qn =null;	
		try {
			qn=RPNameConverter.makeQName(rp);
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		GetResourcePropertyResponse rpr= null;	
		try {	
			rpr = ((ResourcePropertiesPortType)service).getResourceProperty(qn);
			
		} catch (InvalidResourcePropertyQNameFaultType e) {
			e.printStackTrace();
		} catch (ResourceUnknownFaultType e) {
			e.printStackTrace();
		} catch (RemoteException e) {
			e.printStackTrace();
		}

		Job job=null;
		MessageElement[] rps = rpr.get_any();
		for(int i=0; i<rps.length;i++){
		    
			if(rps[i].getValue()!=null){
				System.out.println("<"+rps[i].getLocalName()+"> "+rps[i].getValue());
			}else {					
				try {
					Object rpo=null;
					
				    if(rps[i].getLocalName().equals("JobStatus"))
				        rpo=rps[i].getValueAsType(RPNameConverter.getQNameType(rps[i].getLocalName()),JobStatusType.class);
				    else
				        rpo=rps[i].getValueAsType(RPNameConverter.getQNameType(rps[i].getLocalName()));
				
					if (rpo instanceof JobStatusType){
					   
					    if(jobName.equals(((JobStatusType)rpo).getJobName())){
					       
					       EndpointReferenceType job_epr= ((JobStatusType)rpo).getJobReference();
					       
					       System.out.println("EPR:Address	= " + job_epr.getAddress());
					       System.out.println("EPR:Ref Prop ResourceId	= "+ (job_epr.getProperties() == null? "null props": job_epr.getProperties().get_any()[0].getAsString()));
						  
					       JobServiceAddressingLocator job_service = new JobServiceAddressingLocator();
					       job = job_service.getJobService(job_epr);
					       ((JobSoapBindingStub)job)._setProperty(com.fujitsu.unicore.services.impl.wsa.WSAddressingHeaders.REFERENCE_PROPERTY_NAME, job_epr.getProperties().get_any()[0]);
					   }
					}
				} catch (Exception e) {
					e.printStackTrace();
				}			    
			}			
		}
	    
	    return job;
	}
}
}

//
//							Copyright (c) Fujitsu Ltd 2000 - 2005
//
//						Use and distribution is subject a License.
//			A copy was supplied with the distribution (see documentation or the jar file).
//
//		This product includes software developed by Fujitsu Limited (http://www.fujitsu.com).