<commit_before>package org.javarosa.communication.reliablehttp;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.Thread;

import javax.microedition.io.Connection;
import javax.microedition.io.Connector;
import javax.microedition.io.HttpConnection;
import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.AlertType;

import org.javarosa.core.JavaRosaServiceProvider;
import org.javarosa.core.api.IActivity;
import org.javarosa.core.services.ITransportManager;
import org.javarosa.core.services.transport.IDataPayload;
import org.javarosa.core.services.transport.ITransportDestination;
import org.javarosa.core.services.transport.TransportMessage;
import org.javarosa.core.services.transport.TransportMethod;
import org.javarosa.communication.http.HttpTransportMethod;
import org.javarosa.communication.http.HttpTransportDestination;
import org.javarosa.communication.http.HttpHeaderAppendingVisitor;

/**
 * 
 * @author <a href="mailto:<EMAIL>"><NAME></a>
 */
public class ReliableHttpTransportMethod extends HttpTransportMethod {

	private static final String name = "RELIABLE_HTTP";

	private ITransportManager manager;
	
	private IActivity destinationRetrievalActivity;
	private ReliableWorkerThread primaryWorker;

    //RL: Temporary values until we start using timeouts
    private static final int MAX_NUM_RETRIES = 7; 
    private static final int MD5 = 999; 

    /*
	 * (non-Javadoc)
	 * 
	 * @see org.openmrs.transport.TransportMethod#transmit(org.openmrs.transport.TransportMessage)
	 */
	public void transmit(TransportMessage message, ITransportManager manager) {
		cacheURL(message);		
		this.manager = manager;
		primaryWorker = new ReliableWorkerThread();
		primaryWorker.setMessage(message);	
		new Thread(primaryWorker).start();
	}

	/**
	 * 
	 * @author <a href="mailto:<EMAIL>"><NAME></a>
	 */
	private class ReliableWorkerThread implements Runnable {
		private HttpConnection con = null;
		private InputStream in = null;
		private OutputStream out = null;
		
		private TransportMessage message;
		
		public void setMessage(TransportMessage message) {
			this.message  = message;
		}
		
		
		public void cleanStreams(){
			if (in != null) {
				try {
					in.close();
				} catch (IOException e) {
					// ignore
				}
			}
			if (out != null) {
				try {
					out.close();
				} catch (IOException e) {
					// ignore
				}
			}
			if (con != null) {
				try {
					con.close();
				} catch (IOException e) {
					// ignore
				}
			}
			
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see java.lang.Thread#run()
		 */
		public void run() {
			
            try{
    			IDataPayload payload = message.getPayloadData();
    			HttpHeaderAppendingVisitor visitor = new HttpHeaderAppendingVisitor();
    			IDataPayload httpload = (IDataPayload)payload.accept(visitor);
    
    			HttpTransportDestination destination = (HttpTransportDestination)message.getDestination();
			
    			reliableHttpPost(destination.getURL(),MD5,
		            visitor.getOverallContentType(),httpload);		

				// update status
				message.setStatus(TransportMessage.STATUS_DELIVERED);
				//#if debug.output==verbose
				System.out.println("Status: " + message.getStatus());
				//#endif
				message.setChanged();
				message.notifyObservers(message.getReplyloadData());

			} catch (ClassCastException e) {
				Alert alert = new Alert("ERROR! cce", e.getMessage(), null, AlertType.ERROR);
				throw new IllegalArgumentException(message.getDestination()
						+ " is not a valid HTTP URL");
			} catch (IOException e) {
				Alert alert = new Alert("ERROR! ioe", e.getMessage(), null, AlertType.ERROR);
				//#if debug.output==verbose || debug.output==exception
				System.out.println("Error! ioe: " + e.getMessage());
                e.printStackTrace();
				//#endif
			} catch (OtherIOException e){
                Alert alert = new Alert("ERROR! oioe", e.getMessage(), null, AlertType.ERROR);
                //#if debug.output==verbose || debug.output==exception
                System.out.println("Error! oioe: " + e.getMessage() );
                e.printStackTrace();
                //#endif			  
			} catch(java.lang.SecurityException se) {
				Alert alert = new Alert("ERROR! se", se.getMessage(), null, AlertType.ERROR);
	             /***
                 * This exception was added to deal with the user denying access to airtime
                 */
			 // update status
                message.setStatus(TransportMessage.STATUS_FAILED);
        		//#if debug.output==verbose || debug.output==exception
                System.out.println("Status: " + message.getStatus());
                //#endif
                message.setChanged();
                message.notifyObservers(null); 
		    }finally {
				cleanUp(in);
				cleanUp(out);
				cleanUp(con);
			}
		}

		private void process(byte data) {
			System.out.print(data);
			byte[] temp = new byte[1];
			temp[0] = data;

			message.setReplyloadData(temp);
		}

		private void process(byte[] data) {
			//#if debug.output==verbose
			System.out.println(new String(data));
			//#endif
			message.setReplyloadData(data);
		}
		
		/*
		 * Sends a header of the form
		 * POST /upload HTTP/1.1 / Host: example.com / If-Match: "vEpr6barcD" / Content-Length: 100 / Content-Range: 0-99/100
		 */
		private void reliableHttpPost(String url, int MD5, String contentType, IDataPayload pl)
		    throws IOException, OtherIOException {
		    boolean sendFailed;
            InputStream in = pl.getPayloadStream();

		    //Eventually we will use timeouts instead of maximum number of retries
		    int numTries = 0;
            while (numTries<MAX_NUM_RETRIES){
                numTries++;
                sendFailed = false;
                //Posting to a non-existent machine should generate an exception in the following code

                int lastByte = reliableRequestLastByte(url, MD5);
                if (lastByte != -1 ){
                    //server supports reliable http and has returned a valid lastByte
                    in.skip(lastByte);
                }
                
                //#if debug.output==verbose || debug.output==exception
                try{
                //#endif
                    con = (HttpConnection) Connector.open(url);
                    con.setRequestMethod(HttpConnection.POST);
                    setDefaultRequestProperties(con);
                    con.setRequestProperty("Content-Type",contentType);
                    con.setRequestProperty("If-Match",String.valueOf(MD5));
                    out = con.openOutputStream(); // Problem exists here on 3110c CommCare Application: open hangs
                //#if debug.output==verbose || debug.output==exception
                } catch (IOException e){
                    throw new IOException("reliableHttpPost: error in initialization");
                }
                //#endif
                
                //We only want to deal with errors in streaming files
                try{
                    //RL: This should be optimized to read in packets of 1450 bytes at a time
                    int c = inputStreamReadCastExceptions(in);
                    while(c != -1) {
                        out.write(c);
                        c = inputStreamReadCastExceptions(in);
                    }
                    out.flush();	   
                    
                    //Check whether we have received the final ACK of this file
                    int responseCode = con.getResponseCode();<commit_msg>[r1850] fix to make this code work faster when interacting with a regular non-reliable http server<commit_after>package org.javarosa.communication.reliablehttp;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.Thread;

import javax.microedition.io.Connection;
import javax.microedition.io.Connector;
import javax.microedition.io.HttpConnection;
import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.AlertType;

import org.javarosa.core.JavaRosaServiceProvider;
import org.javarosa.core.api.IActivity;
import org.javarosa.core.services.ITransportManager;
import org.javarosa.core.services.transport.IDataPayload;
import org.javarosa.core.services.transport.ITransportDestination;
import org.javarosa.core.services.transport.TransportMessage;
import org.javarosa.core.services.transport.TransportMethod;
import org.javarosa.communication.http.HttpTransportMethod;
import org.javarosa.communication.http.HttpTransportDestination;
import org.javarosa.communication.http.HttpHeaderAppendingVisitor;

/**
 * 
 * @author <a href="mailto:<EMAIL>"><NAME></a>
 */
public class ReliableHttpTransportMethod extends HttpTransportMethod {

	private static final String name = "RELIABLE_HTTP";

	private ITransportManager manager;
	
	private IActivity destinationRetrievalActivity;
	private ReliableWorkerThread primaryWorker;

    //RL: Temporary values until we start using timeouts
    private static final int MAX_NUM_RETRIES = 7; 
    private static final int MD5 = 999; 

    /*
	 * (non-Javadoc)
	 * 
	 * @see org.openmrs.transport.TransportMethod#transmit(org.openmrs.transport.TransportMessage)
	 */
	public void transmit(TransportMessage message, ITransportManager manager) {
		cacheURL(message);		
		this.manager = manager;
		primaryWorker = new ReliableWorkerThread();
		primaryWorker.setMessage(message);	
		new Thread(primaryWorker).start();
	}

	/**
	 * 
	 * @author <a href="mailto:<EMAIL>"><NAME></a>
	 */
	private class ReliableWorkerThread implements Runnable {
		private HttpConnection con = null;
		private InputStream in = null;
		private OutputStream out = null;
		
		private TransportMessage message;
		
		public void setMessage(TransportMessage message) {
			this.message  = message;
		}
		
		
		public void cleanStreams(){
			if (in != null) {
				try {
					in.close();
				} catch (IOException e) {
					// ignore
				}
			}
			if (out != null) {
				try {
					out.close();
				} catch (IOException e) {
					// ignore
				}
			}
			if (con != null) {
				try {
					con.close();
				} catch (IOException e) {
					// ignore
				}
			}
			
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see java.lang.Thread#run()
		 */
		public void run() {
			
            try{
    			IDataPayload payload = message.getPayloadData();
    			HttpHeaderAppendingVisitor visitor = new HttpHeaderAppendingVisitor();
    			IDataPayload httpload = (IDataPayload)payload.accept(visitor);
    
    			HttpTransportDestination destination = (HttpTransportDestination)message.getDestination();
			
    			reliableHttpPost(destination.getURL(),MD5,
		            visitor.getOverallContentType(),httpload);		

				// update status
				message.setStatus(TransportMessage.STATUS_DELIVERED);
				//#if debug.output==verbose
				System.out.println("Status: " + message.getStatus());
				//#endif
				message.setChanged();
				message.notifyObservers(message.getReplyloadData());

			} catch (ClassCastException e) {
				Alert alert = new Alert("ERROR! cce", e.getMessage(), null, AlertType.ERROR);
				throw new IllegalArgumentException(message.getDestination()
						+ " is not a valid HTTP URL");
			} catch (IOException e) {
				Alert alert = new Alert("ERROR! ioe", e.getMessage(), null, AlertType.ERROR);
				//#if debug.output==verbose || debug.output==exception
				System.out.println("Error! ioe: " + e.getMessage());
                e.printStackTrace();
				//#endif
			} catch (OtherIOException e){
                Alert alert = new Alert("ERROR! oioe", e.getMessage(), null, AlertType.ERROR);
                //#if debug.output==verbose || debug.output==exception
                System.out.println("Error! oioe: " + e.getMessage() );
                e.printStackTrace();
                //#endif			  
			} catch(java.lang.SecurityException se) {
				Alert alert = new Alert("ERROR! se", se.getMessage(), null, AlertType.ERROR);
	             /***
                 * This exception was added to deal with the user denying access to airtime
                 */
			 // update status
                message.setStatus(TransportMessage.STATUS_FAILED);
        		//#if debug.output==verbose || debug.output==exception
                System.out.println("Status: " + message.getStatus());
                //#endif
                message.setChanged();
                message.notifyObservers(null); 
		    }finally {
				cleanUp(in);
				cleanUp(out);
				cleanUp(con);
			}
		}

		private void process(byte data) {
			System.out.print(data);
			byte[] temp = new byte[1];
			temp[0] = data;

			message.setReplyloadData(temp);
		}

		private void process(byte[] data) {
			//#if debug.output==verbose
			System.out.println(new String(data));
			//#endif
			message.setReplyloadData(data);
		}
		
		/*
		 * Sends a header of the form
		 * POST /upload HTTP/1.1 / Host: example.com / If-Match: "vEpr6barcD" / Content-Length: 100 / Content-Range: 0-99/100
		 */
		private void reliableHttpPost(String url, int MD5, String contentType, IDataPayload pl)
		    throws IOException, OtherIOException {
            InputStream in = pl.getPayloadStream();

		    //Eventually we will use timeouts instead of maximum number of retries
		    int numTries = 0;
            boolean sendFailed, isNotReliableServer = false;
            while (numTries<MAX_NUM_RETRIES){
                numTries++;
                sendFailed = false;

                if(!isNotReliableServer){
                    int lastByte = reliableRequestLastByte(url, MD5);
                    if (lastByte == -1 ){
                        isNotReliableServer = true;
                    } else {
                        in.skip(lastByte);
                    }
                }
                
                //#if debug.output==verbose || debug.output==exception
                try{
                //#endif
                    //Posting to a non-existent machine should generate an exception in the following code
                    con = (HttpConnection) Connector.open(url);
                    con.setRequestMethod(HttpConnection.POST);
                    setDefaultRequestProperties(con);
                    con.setRequestProperty("Content-Type",contentType);
                    con.setRequestProperty("If-Match",String.valueOf(MD5));
                    out = con.openOutputStream(); // Problem exists here on 3110c CommCare Application: open hangs
                //#if debug.output==verbose || debug.output==exception
                } catch (IOException e){
                    throw new IOException("reliableHttpPost: error in initialization");
                }
                //#endif
                
                //We only want to deal with errors in streaming files
                try{
                    //RL: This should be optimized to read in packets of 1450 bytes at a time
                    int c = inputStreamReadCastExceptions(in);
                    while(c != -1) {
                        out.write(c);
                        c = inputStreamReadCastExceptions(in);
                    }
                    out.flush();	   
                    
                    //Check whether we have received the final ACK of this file
                    int responseCode = con.getResponseCode();
{'fraction_non_alphanumeric': 0.10617848970251716, 'fraction_numerical': 0.004707420725727362, 'mean_word_length': 2.3324618736383442, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2376970', 'n_tokens_mistral': 4762, 'n_tokens_neox': 4070, 'n_words': 970}