/**
* galaxy inc.
* meetup client for android
*/
package com.galaxy.meetup.client.android.network.http;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Vector;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import org.apache.http.Header;
import org.apache.http.client.HttpResponseException;
import org.apache.http.cookie.Cookie;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import com.galaxy.meetup.client.android.content.EsAccount;
import com.galaxy.meetup.client.android.content.EsNetworkData;
import com.galaxy.meetup.client.android.network.MemoryException;
import com.galaxy.meetup.client.android.network.NetworkException;
import com.galaxy.meetup.client.android.network.http.HttpTransaction.HttpTransactionListener;
import com.galaxy.meetup.client.android.service.EsSyncAdapterService;
import com.galaxy.meetup.client.android.thread.MeetupThreadFactory;
import com.galaxy.meetup.client.util.EsLog;
/**
*
* @author sihai
*
*/
public class HttpOperation implements HttpTransactionListener {
private static final Vector<byte[]> sBufferCache;
private static final ExecutorService sExecutorService;
protected static final Handler sHandler = new Handler(Looper.getMainLooper());
private static Exception sSimulateException = null;
private static final ThreadFactory sThreadFactory;
private boolean mAborted;
protected final EsAccount mAccount;
protected final Context mContext;
private volatile HttpTransaction mCurrentTransaction;
private int mErrorCode;
private Exception mEx;
private final HttpRequestConfiguration mHttpRequestConfig;
private final Intent mIntent;
private final OperationListener mListener;
private final String mMethod;
private OutputStream mOutputStream;
private String mReasonPhrase;
private int mRetriesRemaining;
private boolean mThreaded;
private final String mUrl;
static {
sBufferCache = new Vector<byte[]>(1);
for (int i = 0; i <= 0; i++)
sBufferCache.add(new byte[2048]);
sThreadFactory = new MeetupThreadFactory("HttpOperation", null, true, Thread.MIN_PRIORITY);
sExecutorService = Executors.newCachedThreadPool(sThreadFactory);
}
//===========================================================================
// Constructor
//===========================================================================
public HttpOperation(Context context, String method, String url, EsAccount esaccount, OutputStream outputstream, Intent intent, OperationListener operationlistener){
this(context, method, url, ((HttpRequestConfiguration) (new DefaultHttpRequestConfiguration(context, esaccount))), esaccount, outputstream, intent, operationlistener);
}
public HttpOperation(Context context, String method, String url, HttpRequestConfiguration httprequestconfiguration, EsAccount esaccount, OutputStream outputstream, Intent intent,
OperationListener operationlistener) {
mErrorCode = -1;
mRetriesRemaining = 2;
mContext = context;
mMethod = method;
mUrl = url;
mHttpRequestConfig = httprequestconfiguration;
mAccount = esaccount;
mOutputStream = outputstream;
mIntent = intent;
mListener = operationlistener;
}
//===========================================================================
// Public function
//===========================================================================
public final void start() {
if (EsLog.ENABLE_DOGFOOD_FEATURES) {
HttpTransactionMetrics httptransactionmetrics = new HttpTransactionMetrics();
start(null, httptransactionmetrics);
httptransactionmetrics.log("HttpTransaction", "");
} else {
start(null, null);
}
}
public void start(EsSyncAdapterService.SyncState syncstate, HttpTransactionMetrics httptransactionmetrics) {
if(mAborted) {
if(syncstate != null && httptransactionmetrics != null)
syncstate.getHttpTransactionMetrics().accumulateFrom(httptransactionmetrics);
return;
}
//if(EsLog.isLoggable("HttpTransaction", 3))
Log.d("HttpTransaction", (new StringBuilder("Starting op: ")).append(mUrl).toString());
try {
MeetupRequest request = createPostData();
HttpTransaction httptransaction = null;
if(null == request) {
httptransaction = new HttpTransaction(mMethod, mUrl, mHttpRequestConfig, (HttpTransactionListener)this);
} else {
httptransaction = new HttpTransaction(mMethod, mUrl, mHttpRequestConfig, request, (HttpTransactionListener)this);
}
if(EsLog.isLoggable("HttpTransaction", 3))
httptransaction.printHeaders();
if(httptransactionmetrics != null)
httptransactionmetrics.onBeginTransaction(getName());
httptransaction.setHttpTransactionMetrics(httptransactionmetrics);
mCurrentTransaction = httptransaction;
httptransaction.execute();
EsNetworkData.insertData(mContext, mAccount, httptransactionmetrics, null);
mCurrentTransaction = null;
if(syncstate != null && httptransactionmetrics != null)
syncstate.getHttpTransactionMetrics().accumulateFrom(httptransactionmetrics);
// TODO
} catch (IOException e) {
Log.e("ERROR", e.getMessage());
onHttpTransactionComplete(0, null, e);
} finally {
}
}
public final void startThreaded() {
mThreaded = true;
sExecutorService.execute(new Runnable() {
public final void run() {
start();
}
});
}
public final void abort() {
mAborted = true;
HttpTransaction httptransaction = mCurrentTransaction;
if(httptransaction != null)
httptransaction.abort();
}
public final EsAccount getAccount()
{
return mAccount;
}
public final int getErrorCode()
{
return mErrorCode;
}
public final Exception getException()
{
return mEx;
}
public final Intent getIntent()
{
return mIntent;
}
public final String getMethod()
{
return mMethod;
}
public String getName()
{
return getClass().getSimpleName();
}
public final OutputStream getOutputStream()
{
return mOutputStream;
}
public final String getReasonPhrase()
{
return mReasonPhrase;
}
public final String getUrl()
{
return mUrl;
}
public boolean hasError()
{
boolean flag;
if(mErrorCode != 200)
flag = true;
else
flag = false;
return flag;
}
public final boolean isAborted()
{
return mAborted;
}
public final void setOutputStream(OutputStream outputstream) {
mOutputStream = outputstream;
}
public void setErrorInfo(int i, String s, Exception exception) {
mErrorCode = i;
mReasonPhrase = s;
mEx = exception;
}
public final void logAndThrowExceptionIfFailed(String s) throws IOException {
if (hasError()) {
logError(s);
if (hasError()) {
if (mEx != null)
if (android.os.Build.VERSION.SDK_INT < 9)
throw new IOException((new StringBuilder())
.append(getName()).append(" operation failed ")
.append(mEx.getMessage()).toString());
else
throw new IOException((new StringBuilder())
.append(getName()).append(" operation failed")
.toString(), mEx);
if (hasError())
throw new IOException((new StringBuilder())
.append(getName())
.append(" operation failed, error: ")
.append(mErrorCode).append(" [")
.append(mReasonPhrase).append("]").toString());
}
}
}
public void logError(String s) {
if(null != mEx) {
Log.e(s, (new StringBuilder("[")).append(getName()).append("] failed due to exception: ").append(mEx).toString(), mEx);
return;
}
if(hasError() && EsLog.isLoggable(s, 4))
Log.i(s, (new StringBuilder("[")).append(getName()).append("] failed due to error: ").append(mErrorCode).append(" [").append(mReasonPhrase).append("]").toString());
}
@Override
public void onHttpCookie(Cookie cookie) {
// TODO Auto-generated method stub
}
public void onHttpOperationComplete(int i, String s, Exception exception) {
}
@Override
public void onHttpReadErrorFromStream(InputStream inputstream, String s,
int i, Header[] aheader, int j) throws IOException {
// TODO Auto-generated method stub
}
public void onHttpReadFromStream(InputStream inputstream, String s, int i, Header aheader[]) throws IOException {
OutputStream outputstream = mOutputStream;
if (outputstream != null) {
readFromStream(inputstream, i, outputstream);
//onHttpHandleContentFromStream(null);
} else if (s != null)
onHttpHandleContentFromStream(inputstream);
else
Log.e("HttpTransaction", "Content type not specified");
}
public final void onHttpTransactionComplete(final int errorCode, final String reasonPhrase, Exception exception) {
Exception result = exception;
try {
if(!isImmediatelyRetryableError(exception) || mRetriesRemaining <= 0)
return;
if(isAuthenticationError(exception))
mHttpRequestConfig.invalidateAuthToken();
Log.i("HttpTransaction", "====> Restarting operation...");
mRetriesRemaining = -1 + mRetriesRemaining;
try {
start();
} catch (Exception e) {
result = e;
Log.e("HttpTransaction", "====> Retry failed");
e.printStackTrace();
}
} finally {
final Exception fex = result;
if(mThreaded) {
sHandler.post(new Runnable() {
public final void run()
{
completeOperation(errorCode, reasonPhrase, fex);
}
});
} else {
completeOperation(errorCode, reasonPhrase, fex);
}
}
}
public final void onStartResultProcessing() {
if(mCurrentTransaction != null)
mCurrentTransaction.onStartResultProcessing();
}
//===========================================================================
// Protected function
//===========================================================================
protected boolean isAuthenticationError(Exception exception) {
if(!(exception instanceof HttpResponseException)) {
return false;
}
return 401 == ((HttpResponseException)exception).getStatusCode();
}
protected boolean isImmediatelyRetryableError(Exception exception) {
return isAuthenticationError(exception);
}
protected static InputStream captureResponse(InputStream inputstream, int i, StringBuilder stringbuilder) throws IOException {
ByteArrayOutputStream bytearrayoutputstream = new ByteArrayOutputStream();
readFromStream(inputstream, i, bytearrayoutputstream);
byte abyte0[] = bytearrayoutputstream.toByteArray();
stringbuilder.append(new String(abyte0));
return new ByteArrayInputStream(abyte0);
}
public MeetupRequest createPostData() throws IOException {
return null;
}
public void onHttpHandleContentFromStream(InputStream inputstream) throws IOException {
}
//===========================================================================
// Private function
//===========================================================================
private void completeOperation(int i, String s, Exception exception)
{
setErrorInfo(i, s, exception);
if(mListener != null)
mListener.onOperationComplete(this);
}
private static void readFromStream(InputStream inputstream, int i, OutputStream outputstream) throws NetworkException, MemoryException {
try {
byte[] abyte0 = null;
boolean flag = false;
try {
abyte0 = (byte[])sBufferCache.remove(0);
flag = true;
} catch (IndexOutOfBoundsException e) {
abyte0 = new byte[2048];
flag = false;
}
if(i != -1) {
do {
int i1 = inputstream.read(abyte0, 0, abyte0.length);
if(i1 == -1)
break;
outputstream.write(abyte0, 0, i1);
} while(true);
} else {
int rest = i;
int readed = 0;
try {
while(rest > 0) {
readed = inputstream.read(abyte0, 0, Math.min(rest, abyte0.length));
if(readed == -1)
throw new NetworkException((new StringBuilder("Invalid content length: ")).append(rest).toString());
else if(readed == 0) {
break;
}
rest -= readed;
outputstream.write(abyte0, 0, readed);
}
} catch (OutOfMemoryError e) {
throw new MemoryException(e.getMessage());
}
}
if(flag)
sBufferCache.add(abyte0);
} catch (IOException e) {
throw new NetworkException(e.getMessage());
} finally {
try {
inputstream.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
outputstream.flush();
} catch (IOException e) {
e.printStackTrace();
}
try {
outputstream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//===========================================================================
// Inner class
//===========================================================================
public static interface OperationListener {
void onOperationComplete(HttpOperation httpoperation);
}
}