All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.couchbase.lite.util.Utils Maven / Gradle / Ivy

package com.couchbase.lite.util;

import com.couchbase.lite.CouchbaseLiteException;
import com.couchbase.lite.Status;
import com.couchbase.lite.internal.InterfaceAudience;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpResponseException;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

public class Utils {

    public static int DEFAULT_TIME_TO_WAIT_4_SHUTDOWN = 20;
    public static int DEFAULT_TIME_TO_WAIT_4_SHUTDOWNNOW = 20;

    /**
     * Like equals, but works even if either/both are null
     *
     * @param obj1 object1 being compared
     * @param obj2 object2 being compared
     * @return true if both are non-null and obj1.equals(obj2), or true if both are null.
     * otherwise return false.
     */
    public static boolean isEqual(Object obj1, Object obj2) {
        if (obj1 != null) {
            return (obj2 != null) && obj1.equals(obj2);
        } else {
            return obj2 == null;
        }
    }

    /**
     * in CBLMisc.m
     * BOOL CBLIsPermanentError( NSError* error )
     */
    public static boolean isPermanentError(Throwable throwable) {
        if (throwable instanceof CouchbaseLiteException) {
            CouchbaseLiteException e = (CouchbaseLiteException) throwable;
            return isPermanentError(e.getCBLStatus().getCode());
        } else if (throwable instanceof HttpResponseException) {
            HttpResponseException e = (HttpResponseException) throwable;
            return isPermanentError(e.getStatusCode());
        } else {
            return false;
        }
    }

    public static boolean isPermanentError(int code) {
        // 406 - in Test cases, server return 406 because of CouchDB API
        //       http://docs.couchdb.org/en/latest/api/database/bulk-api.html
        //       GET /{db}/_all_docs or POST /{db}/_all_docs
        // 408 - Listener might encounter SocketTimeout under weak network connectivity
        return (code >= 400 && code <= 405) || (code == 407) || (code >= 409 && code <= 499);
    }

    /**
     * in CBLMisc.m
     * BOOL CBLMayBeTransientError( NSError* error )
     */
    public static boolean isTransientError(Throwable throwable) {
        if (throwable instanceof CouchbaseLiteException) {
            CouchbaseLiteException e = (CouchbaseLiteException) throwable;
            return isTransientError(e.getCBLStatus().getCode());
        } else if (throwable instanceof HttpResponseException) {
            HttpResponseException e = (HttpResponseException) throwable;
            return isTransientError(e.getStatusCode());
        } else if (throwable instanceof java.net.SocketTimeoutException)
            // connection and socket timeouts => transient error
            return true;
        else if (throwable instanceof org.apache.http.conn.ConnectTimeoutException)
            return true;
        else if (throwable instanceof java.net.ConnectException)
            return true;
        else if (throwable instanceof java.net.SocketException)
            return true;
        else if (throwable instanceof org.apache.http.conn.HttpHostConnectException)
            return true;
        else if (throwable instanceof IOException)
            // NOTE: Exception is thrown from HttpClient.execute() or InputStream.read()
            //       As InputStream.read() thrown IOException, So IOException is considered
            //       temporary issue.
            return true;
        else
            return false;
    }

    public static boolean isTransientError(StatusLine status) {
        // TODO: in ios implementation, it considers others errors
        //if ($equal(domain, NSURLErrorDomain)) {
        //    return code == NSURLErrorTimedOut
        //            || code == NSURLErrorCannotConnectToHost
        //            || code == NSURLErrorNetworkConnectionLost;

        return isTransientError(status.getStatusCode());
    }

    public static boolean isTransientError(int statusCode) {
        if (statusCode == 500 || statusCode == 502 || statusCode == 503 || statusCode == 504 ||
                statusCode == Status.REQUEST_TIMEOUT) {
            return true;
        }
        return false;
    }

    public static boolean isDocumentError(Throwable throwable) {

        if (throwable instanceof CouchbaseLiteException) {
            CouchbaseLiteException e = (CouchbaseLiteException) throwable;
            return isDocumentError(e.getCBLStatus().getCode());
        } else if (throwable instanceof HttpResponseException) {
            HttpResponseException e = (HttpResponseException) throwable;
            return isDocumentError(e.getStatusCode());
        } else {
            return false;
        }
    }

    public static boolean isDocumentError(int statusCode) {
        return (statusCode == Status.NOT_FOUND || statusCode == Status.FORBIDDEN || statusCode == Status.GONE) ? true : false;
    }

    /**
     * cribbed from http://stackoverflow.com/questions/9655181/convert-from-byte-array-to-hex-string-in-java
     */
    final protected static char[] hexArray = "0123456789abcdef".toCharArray();

    public static String bytesToHex(byte[] bytes) {
        char[] hexChars = new char[bytes.length * 2];
        for (int j = 0; j < bytes.length; j++) {
            int v = bytes[j] & 0xFF;
            hexChars[j * 2] = hexArray[v >>> 4];
            hexChars[j * 2 + 1] = hexArray[v & 0x0F];
        }
        return new String(hexChars);
    }

    public static void assertNotNull(Object o, String errMsg) {
        if (o == null) {
            throw new IllegalArgumentException(errMsg);
        }
    }

    @InterfaceAudience.Private
    public static boolean is404(Throwable e) {
        if (e instanceof HttpResponseException) {
            return ((HttpResponseException) e).getStatusCode() == 404;
        }
        return false;
    }

    @InterfaceAudience.Private
    public static int getStatusFromError(Throwable t) {
        if (t instanceof CouchbaseLiteException) {
            CouchbaseLiteException couchbaseLiteException = (CouchbaseLiteException) t;
            return couchbaseLiteException.getCBLStatus().getCode();
        } else if (t instanceof HttpResponseException) {
            HttpResponseException responseException = (HttpResponseException) t;
            return responseException.getStatusCode();
        }
        return Status.UNKNOWN;
    }

    public static String shortenString(String orig, int maxLength) {
        if (orig == null || orig.length() <= maxLength) {
            return orig;
        }
        return orig.substring(0, maxLength);
    }

    // check if contentEncoding is gzip
    public static boolean isGzip(HttpEntity entity) {
        return isGzip(entity.getContentEncoding());
    }

    // check if contentEncoding is gzip
    public static boolean isGzip(Header contentEncoding) {
        return contentEncoding != null && isGzip(contentEncoding.getValue());
    }

    // check if contentEncoding is gzip
    public static boolean isGzip(String contentEncoding) {
        return contentEncoding != null && contentEncoding.contains("gzip");
    }

    // to gzip
    public static byte[] compressByGzip(byte[] sourceBytes) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            GZIPOutputStream gzip = null;
            try {
                gzip = new GZIPOutputStream(out);
                gzip.write(sourceBytes);
                gzip.close();
            } catch (IOException ex) {
                return null;
            } finally {
                try {
                    if (gzip != null) {
                        gzip.close();
                    }
                } catch (IOException ex) {
                }
            }

            return out.toByteArray();
        } finally {
            try {
                out.close();
            } catch (IOException ex) {
            }
        }
    }

    // from gzip
    public static int CHUNK_SIZE = 8192; // 1024 * 8

    public static byte[] decompressByGzip(byte[] sourceBytes) {
        byte[] buffer = null;
        ByteArrayOutputStream out = null;
        ByteArrayInputStream in = null;
        GZIPInputStream gzip = null;
        try {
            out = new ByteArrayOutputStream();
            buffer = new byte[CHUNK_SIZE];
            in = new ByteArrayInputStream(sourceBytes);
            gzip = new GZIPInputStream(in);
            int len = 0;
            while ((len = gzip.read(buffer, 0, CHUNK_SIZE)) != -1) {
                out.write(buffer, 0, len);
            }
            return out.toByteArray();
        } catch (IOException ex) {
            Log.w(Log.TAG, "Failed to decompress gzipped data: " + ex.getMessage());
            return null;
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
            } catch (IOException ex) {
            }
            try {
                if (in != null) {
                    in.close();
                }
            } catch (IOException ex) {
            }
            try {
                if (gzip != null) {
                    gzip.close();
                }
            } catch (IOException ex) {
            }
        }
    }

    public static Map headersToMap(Header[] headers) {
        Map map = new TreeMap(String.CASE_INSENSITIVE_ORDER);
        for (int i = 0; i < headers.length; i++) {
            map.put(headers[i].getName(), headers[i].getValue());
        }
        return map;
    }

    /**
     * The following method shuts down an ExecutorService in two phases,
     * first by calling shutdown to reject incoming tasks, and then calling shutdownNow,
     * if necessary, to cancel any lingering tasks:
     * http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ExecutorService.html
     *
     * @param timeToWait4ShutDown - Seconds
     * @param timeToWait4ShutDownNow - Seconds
     */
    public static void shutdownAndAwaitTermination(ExecutorService pool,
                                                   long timeToWait4ShutDown,
                                                   long timeToWait4ShutDownNow) {
        synchronized (pool) {
            // Disable new tasks from being submitted
            pool.shutdown();
        }
        try {
            // Wait a while for existing tasks to terminate
            if (!pool.awaitTermination(timeToWait4ShutDown, TimeUnit.SECONDS)) {
                synchronized (pool) {
                    pool.shutdownNow(); // Cancel currently executing tasks
                }
                // Wait a while for tasks to respond to being cancelled
                if (!pool.awaitTermination(timeToWait4ShutDownNow, TimeUnit.SECONDS)) {
                    Log.e(Log.TAG_DATABASE, "Pool did not terminate");
                }
            }
        } catch (InterruptedException ie) {
            // (Re-)Cancel if current thread also interrupted
            synchronized (pool) {
                pool.shutdownNow();
            }
            // Preserve interrupt status
            Thread.currentThread().interrupt();
        }
    }

    public static void shutdownAndAwaitTermination(ExecutorService pool) {
        shutdownAndAwaitTermination(pool,
                DEFAULT_TIME_TO_WAIT_4_SHUTDOWN,
                DEFAULT_TIME_TO_WAIT_4_SHUTDOWNNOW);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy