package examples.mega.basic;

import java.net.URL;

import java.io.IOException;
import java.io.InputStream;
import java.io.ByteArrayOutputStream;

import java.rmi.RemoteException;
import javax.xml.rpc.ServiceException;

import java.util.Date;
import java.util.Calendar;
import java.util.ArrayList;

import javax.activation.DataHandler;

import javax.xml.rpc.holders.StringHolder;
import javax.xml.rpc.holders.IntHolder;
import javax.xml.rpc.holders.ShortHolder;
import javax.xml.rpc.holders.FloatHolder;
import javax.xml.rpc.holders.LongHolder;
import javax.xml.rpc.holders.DoubleHolder;
import javax.xml.rpc.holders.CalendarHolder;

import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeBodyPart;

import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;

import javax.xml.namespace.QName;

import javax.xml.rpc.soap.SOAPFaultException;
import javax.xml.rpc.handler.soap.SOAPMessageContext;
import javax.xml.soap.SOAPMessage;

import weblogic.webservice.context.WebServiceContext;
import weblogic.webservice.context.ContextNotFoundException;
import weblogic.webservice.context.WebServiceHeader;
import weblogic.webservice.context.WebServiceSession;

import weblogic.webservice.async.FutureResult;

/** 
 * MegaService - a service to show different features of 
 * Weblogic webservice stack.
 * 
 * @wlws:webservice targetNamespace="http://www.bea.com/mega-service/"
 *    name="MegaWebService"
 *    portName="MegaPort"
 *    portTypeName="MegaPort"
 *    charset = "UTF-8"
 *    exposeWSDL="true"
 */
public class MegaService{


  //*************
  // BASIC METHOD
  //*************

  /**
   * A simple call that returns "helloWorld". No special java doc tag
   * needed. All public methods are exposed as webservice by default. 
   */
  public String helloWorld(){
    return "helloWorld";
  }

  //*************
  // ONE-WAY CALL 
  //*************
  
  /** 
   * A one way call. Client will not wait for this method to
   * complete.
   *
   * Note: A one way call must have a void return type.
   *
   * @wlws:operation 
   *       invocation-style="one-way"
   */
  public void oneWayCall( long time ){
    weblogic.utils.Debug.say( "(manoj):oneWayCall time:" + time );
  }

  //**********************
  // EXCLUDE PUBLIC METHOD 
  //**********************

  /** 
   * A public method that is not exposed as web service method.
   *
   * @wlws:exclude 
   */
  public void dontExposeThisMethod(){
  }

  //******************
  // OVERLOADED METHOD 
  //******************

  /** 
   * Overloaded first method.
   */
  public boolean overloadedMethod( boolean called ){
    return called;
  }

  /** 
   * Overloaded secound method. Name of this method is changed, because
   * it is overloaded.
   *
   * Note: method overloading is supported by mangling the method 
   * name. ie, WSDL overloading is not supported, only the overloading
   * of methods in the end component is supported.
   *
   * @wlws:operation
   *       name="overloadedSoMadeDifferent"
   */
  public String overloadedMethod( String called ){
    return called;
  }

  //***********
  // DATA TYPES 
  //***********

  /** 
   * method with primitive types 
   */
  public BaseData primitiveTypes( int id, float salary, char sex, 
      String address, double balance, long pin, short length, Date date ){

    BaseData data = new BaseData();

    data.setId( id );
    data.setSalary( salary );
    data.setSex( sex );
    data.setAddress( address );
    data.setBalance( balance );
    data.setPin( pin );
    data.setLength( length );
    data.setDate( date );

    return data;
  }

  /** 
   * method with wrapper types 
   */
  public BaseData wrapperTypes( Integer id, Float salary, Character sex, 
      String address, Double balance, Long pin, Short length, Date date ){

    BaseData data = new BaseData();

    data.setId( id.intValue() );
    data.setSalary( salary.floatValue() );
    data.setSex( sex.charValue() );
    data.setAddress( address );
    data.setBalance( balance.doubleValue() );
    data.setPin( pin.longValue() );
    data.setLength( length.shortValue() );
    data.setDate( date );

    return data;
  }

  /**
   * complex type
   */
  public BaseData complexType( BaseData data ){
    return data;
  }

  /** 
   * method with array of primitive types 
   */
  public BaseData primitiveArrays( int[] id, float[] salary, char[] sex, 
      String[] address, double[] balance, long[] pin, short[] length, 
      Date[] date ){

    BaseData data = new BaseData();

    data.setIdArray( id );
    data.setSalaryArray( salary );
    data.setSexArray( sex );
    data.setAddressArray( address );
    data.setBalanceArray( balance );
    data.setPinArray( pin );
    data.setLengthArray( length );
    data.setDateArray( date );

    return data;
  }

  /** 
   * method with generic types 
   */
  public ArrayList genericTypes( Object obj, Object[] objArray ){
    ArrayList list = new ArrayList();
    list.add( obj );

    for( int i=0; i<objArray.length; i++ ){
      list.add( objArray[i] );
    }

    return list;
  }


  //************
  // SOAP HEADER
  //************


  /** 
   * operation with headers
   *
   * @wlws:part addressInHeader location="header" 
   * @wlws:part dataInHeader location="header" 
   *
   * @wlws:part return location="body" 
   */
  public BaseData methodWithHeaders( String addressInHeader, int idInBody, 
      BaseData dataInHeader ){

    dataInHeader.setAddress( addressInHeader );
    dataInHeader.setId( idInBody );

    return dataInHeader;
  }

  //***********
  // ATTACHMENT 
  //***********

  /**
   * method with attachments
   *
   * @wlws:part xmlAttachment location="attachment"
   * @wlws:part someOtherAttachment location="attachment"
   * @wlws:part return location="attachment"
   */
  public DataHandler methodWithAttachments( String normalString, 
      Source xmlAttachment, DataHandler someOtherAttachment )
      throws IOException{

    StringBuffer sb = new StringBuffer();

    sb.append( normalString );

    ByteArrayOutputStream bout = new ByteArrayOutputStream();

    if( xmlAttachment instanceof StreamSource ){
      InputStream in = ((StreamSource)xmlAttachment).getInputStream();

      int ch;
      while( (ch = in.read()) != -1 ){
        bout.write( (char)ch );
      }

      in.close();
    }

    bout.flush();
    sb.append( new String( bout.toByteArray() ) );
    bout.close();

    sb.append( (String)someOtherAttachment.getContent() );
    return new DataHandler( sb.toString(), "text/plain" );
  }
    
  //***********
  // EXCEPTION 
  //***********

  public void methodThrowingRuntimeException(){
    throw new RuntimeException( "This is a runtime application exception" );
  }

  public void methodThrowingSoapFault() throws SOAPFaultException{

    throw new SOAPFaultException( 
        new QName( "http://schemas.xmlsoap.org/soap/envelope/", "Server" ),
        "oops my fault",
        "some actor",
        weblogic.webservice.util.FaultUtil.newDetail() );
  }

  public void methodThrowingCustomException() throws MegaServiceException{
    throw new MegaServiceException( "Mega service failed" );
  }

  //******************
  // IN-OUT PARAMETERS 
  //******************

  /**
   * by default all Holder classes are in-out
   */
  public BaseData primitiveInOut( IntHolder id, FloatHolder salary, 
      char sex, StringHolder address, DoubleHolder balance, 
      LongHolder pin, ShortHolder length, CalendarHolder date ){

    BaseData data = new BaseData();

    data.setId( id.value );
    data.setSalary( salary.value );
    data.setSex( sex );
    data.setAddress( address.value );
    data.setBalance( balance.value );
    data.setPin( pin.value );
    data.setLength( length.value );
    data.setDate( date.value.getTime() );

    id.value += 1;
    salary.value += 1;
    address.value += 1;
    balance.value += 1;
    pin.value += 1;
    length.value += 1;
    date.value.clear();

    return data;
  }

  //***************
  // OUT PARAMETERS 
  //***************
  
  /**
   * @wlws:part id style="out"
   * @wlws:part salary style="out"
   * @wlws:part address style="out"
   * @wlws:part balance style="out"
   * @wlws:part pin style="out"
   * @wlws:part length style="out"
   * @wlws:part date style="out"
   */
  public void primitiveOut( BaseData data, IntHolder id, FloatHolder salary, 
      StringHolder address, DoubleHolder balance, 
      LongHolder pin, ShortHolder length, CalendarHolder date ){

    id.value = data.getId();
    salary.value = data.getSalary();
    address.value = data.getAddress();
    balance.value = data.getBalance();
    pin.value = data.getPin();
    length.value = data.getLength();
    date.value = Calendar.getInstance();
    date.value.setTime( data.getDate() );
  }

  //**************
  // HANDLER CHAIN 
  //**************

  /**
   * @wlws:operation handler-chain="MegaServiceHandlerChain"
   */
  public void methodWithHandlerChain(){
  }

  //***************************
  // INVOKE EXTERNAL WEBSERVICE 
  //***************************
 
  public void sayHiToExternalService() 
      throws RemoteException, ServiceException, IOException{

        /*
    SimpleTest service = new SimpleTest_Impl();
    SimpleTestSoap port = service.getSimpleTestSoap();
    float result = port.echoFloat( 100.1f );
    System.out.println( "Result from external service:" + result );

    //do async invoke on the external service

    FutureResult futureResult = port.startEchoFloat( 100.1f, null );

    for( int count = 0; count < 10; count++ ){
      if( futureResult.isCompleted() ){
        float asyncResult = port.endEchoFloat( futureResult );

        System.out.println( "Async Result from external service:" + 
            asyncResult );

        return;
      }else{
        System.out.println( "Waiting for result from async invoke:" );  
        try{
          Thread.sleep( 100 );
        }catch( InterruptedException e ){
          e.printStackTrace();
        }
      }
    }

    System.out.println( "Async invoke failed to return result ...... " );  

    */
  }

  /*
   * method to test the performance
   */
  public void v(){}

  //********************
  // WEB SERVICE CONTEXT 
  //********************

  /*
   * This method shows how to access soap message from an
   * end component.
   */
  public void accessWebServiceContext(){
    try{
      WebServiceContext wsContext = WebServiceContext.currentContext();
      SOAPMessageContext messageContext = wsContext.getLastMessageContext();
      SOAPMessage message = messageContext.getMessage();
    }catch( ContextNotFoundException e ){
      e.printStackTrace();
    }
  }

  /*
   * Shows how to use implicit headers
   */
  public BaseData implicitHeader(){
    try{
      WebServiceContext wsContext = WebServiceContext.currentContext();
      WebServiceHeader soapHeader = (WebServiceHeader)wsContext.getHeader();

      BaseData myHeader = (BaseData)soapHeader.get( new QName( 
            "http://www.myheader.namespace/", "my-implicit-header" ) );

      return myHeader;
      
    }catch( ContextNotFoundException e ){
      e.printStackTrace();
      return null; 
    }
  }

  /*
   * Shows how to use HTTP Session to maintain session state between
   * invokes
   */
  public int maintainSessionState(){
    try{
      WebServiceContext wsContext = WebServiceContext.currentContext();
      WebServiceSession session = (WebServiceSession)wsContext.getSession();

      Integer count = (Integer)session.getAttribute( "count" );

      count = (count==null) ? 
        new Integer( 0 ) : new Integer( count.intValue() + 1 );

      session.setAttribute( "count", count );
      return count.intValue();
    }catch( ContextNotFoundException e ){
      e.printStackTrace();
      return -1;
    }
  }

}

