/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */
package foam.blob;

import foam.core.X;

import java.net.URL;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.nio.ByteBuffer;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.InputStreamReader;

public class RestBlobService
    extends AbstractBlobService
{
  public static final int BUFFER_SIZE = 8192;

  protected String address_;

  public RestBlobService(String address) {
    this.address_ = address + "/httpBlobService";
  }

  public RestBlobService(foam.core.X x, String address) {
    setX(x);
    this.address_ = address + "/httpBlobService";
  }

  public String getAddress() {
    return address_;
  }

  @Override
  public Blob put_(X x, Blob blob) {
    if ( blob instanceof IdentifiedBlob ) {
      return blob;
    }
    HttpURLConnection connection = null;
    OutputStream os = null;
    InputStream is = null;
    IdentifiedBlob result = null;
    try {
      URL url = new URL(address_);
      connection = (HttpURLConnection) url.openConnection();
      
      //configure HttpURLConnection
      connection.setConnectTimeout(5 * 1000);
      connection.setReadTimeout(5 * 1000);
      connection.setDoOutput(true);
      connection.setUseCaches(false);

      //set request method
      connection.setRequestMethod("PUT");

      //configure http header
      connection.setRequestProperty("Accept", "*/*");
      connection.setRequestProperty("Connection", "keep-alive");
      connection.setRequestProperty("Content-Type", "application/octet-stream");

      //output blob into connection
      long chunk = 0;
      long size = blob.getSize();
      long chunks = (long) Math.ceil((double) size / (double) BUFFER_SIZE);
      Buffer buffer = new Buffer(BUFFER_SIZE, ByteBuffer.allocate(BUFFER_SIZE));
      os = connection.getOutputStream();

      while ( chunk < chunks ) {
        buffer = blob.read(buffer, chunkOffset(chunk));
        byte[] buf = buffer.getData().array();
        os.write(buf, 0, (int) buffer.getLength());
        buffer.getData().clear();
        chunk++;
      }

      os.flush();

      if ( connection.getResponseCode() == HttpURLConnection.HTTP_OK ) {
        is = connection.getInputStream();
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        String json = "";
        String line = null;
        while ( (line = reader.readLine()) != null ) {
          json += line;
        }

        result = (IdentifiedBlob) (new foam.lib.json.JSONParser()).parseString(json, IdentifiedBlob.class);
        result.setX(getX());
      } else {
        throw new RuntimeException("upload fail");
      }
    } catch ( MalformedURLException e ) {
      e.printStackTrace();
    } catch ( IOException e ) {
      e.printStackTrace();
    } finally {
      closeSource(is, os);
      return result;
    }
  }

  @Override
  public Blob find_(X x, Object id) {
    InputStream is = null;
    HttpURLConnection connection = null;
    Blob blob = null;
    try {
      URL url = new URL(this.address_ + "/" + id.toString());
      connection = (HttpURLConnection) url.openConnection();

      connection.setRequestMethod("GET");
      connection.connect();

      if ( connection.getResponseCode() == HttpURLConnection.HTTP_OK ) {
        is = connection.getInputStream();
        blob = new InputStreamBlob(is);
      } else {
        throw new RuntimeException("download fail");
      }
    } catch ( MalformedURLException e ) {
      e.printStackTrace();
    } catch ( IOException e ) {
      e.printStackTrace();
    } finally {
      closeSource(is, null);
      return blob;
    }
  }

  @Override
  public String urlFor_(X x, Blob blob) {
    if ( ! (blob instanceof IdentifiedBlob) ) {
      return null;
    }
    return this.address_ + "/" + ((IdentifiedBlob) blob).getId();
  }

  private long chunkOffset(long i) {
    return i * BUFFER_SIZE;
  }

  private void closeSource(InputStream is, OutputStream os) {
    if ( os != null ) {
      try {
        os.close();
      } catch ( IOException e ) {
        e.printStackTrace();
      }
    }
    if ( is != null ) {
      try {
        is.close();
      } catch ( IOException e ) {
        e.printStackTrace();
      }
    }
  }
}
@@ -626,48 +626,23 @@
 this
-.address_ = address + %22/httpBlobService%22
+(null, address)
 ;%0A  
@@ -2844,34 +2844,44 @@
 %7B%0A      
-e.printStackTrace(
+throw new RuntimeException(e
 );%0A    %7D
@@ -2913,34 +2913,44 @@
 %7B%0A      
-e.printStackTrace(
+throw new RuntimeException(e
 );%0A    %7D
@@ -2984,16 +2984,28 @@
 e(is, os
+, connection
 );%0A     
@@ -3655,34 +3655,44 @@
 %7B%0A      
-e.printStackTrace(
+throw new RuntimeException(e
 );%0A    %7D
@@ -3724,34 +3724,44 @@
 %7B%0A      
-e.printStackTrace(
+throw new RuntimeException(e
 );%0A    %7D
@@ -3797,16 +3797,28 @@
 is, null
+, connection
 );%0A     
@@ -4168,16 +4168,46 @@
 tream os
+, HttpURLConnection connection
 ) %7B%0A    
@@ -4464,17 +4464,86 @@
     %7D%0A    %7D%0A
+    if ( connection != null ) %7B%0A      connection.disconnect();%0A    %7D%0A
   %7D%0A%7D

fix rethrow exception