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

com.amazonaws.mobileconnectors.amazonmobileanalytics.internal.delivery.FileEventStore Maven / Gradle / Ivy

There is a newer version: 2.20.1
Show newest version
/*
 * Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 *  http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

package com.amazonaws.mobileconnectors.amazonmobileanalytics.internal.delivery;

import android.util.Log;

import com.amazonaws.mobileconnectors.amazonmobileanalytics.internal.core.AnalyticsContext;
import com.amazonaws.mobileconnectors.amazonmobileanalytics.internal.core.system.FileManager;
import com.amazonaws.util.StringUtils;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.concurrent.locks.ReentrantLock;

public class FileEventStore implements EventStore {
    private static final String TAG = "FileEventStore";
    static final String EVENTS_DIRECTORY = "events";
    static final String EVENT_FILE_NAME = "eventsFile";
    static final String KEY_MAX_STORAGE_SIZE = "maxStorageSize";
    static final double ERROR_LENGTH_THRESHOLD_PERCENTAGE = 1.1;
    private final ReentrantLock accessLock = new ReentrantLock(true);

    static long MAX_STORAGE_SIZE = 1024 * 1024 * 5L;

    private final AnalyticsContext context;
    private File eventsFile;

    public static FileEventStore newInstance(final AnalyticsContext context) {
        return new FileEventStore(context);
    }

    public FileEventStore(final AnalyticsContext context) {
        this.context = context;
        tryCreateEventsFile();
    }

    @Override
    public boolean put(final String event) throws EventStoreException {
        boolean success = false;
        BufferedWriter writer = null;

        accessLock.lock();
        try {
            writer = tryInitializeWriter();
            if (writer != null) {
                final long maxStorageSize = context.getConfiguration().optLong(
                        KEY_MAX_STORAGE_SIZE, MAX_STORAGE_SIZE);
                if (eventsFile.length() + event.length() <= maxStorageSize) {
                    writer.write(event);
                    writer.newLine();
                    writer.flush();
                    success = true;
                }
            }
        } catch (IOException e) {
            Log.e(TAG, "Failed to persist the event", e);
        } finally {
            tryCloseWriter(writer);
            accessLock.unlock();
        }

        return success;
    }

    private boolean tryCreateEventsFile() {
        if (eventsFile != null && eventsFile.exists()) {
            return true;
        }

        synchronized (this) {
            if (eventsFile != null && eventsFile.exists()) {
                return true;
            }

            try {
                FileManager fileManager = context.getSystem().getFileManager();
                File eventsDir = fileManager.createDirectory(EVENTS_DIRECTORY);
                eventsFile = fileManager.createFile(new File(eventsDir, EVENT_FILE_NAME));
                return true;
            } catch (IOException e) {
                Log.e(TAG, "Unable to open events file");
                Log.e(TAG, "An error occurred while attempting to create/open the events file", e);
            }
            return false;
        }
    }

    private BufferedWriter tryInitializeWriter() throws EventStoreException {
        BufferedWriter writer = null;
        try {
            if (tryCreateEventsFile()) {
                OutputStream stream = context.getSystem().getFileManager()
                        .newOutputStream(eventsFile, true);
                writer = new BufferedWriter(new OutputStreamWriter(stream, StringUtils.UTF8));
            } else {
                throw new EventStoreException("Unable to create eventsFile");
            }
        } catch (EventStoreException e) {
            throw e;
        } catch (FileNotFoundException e) {
            Log.e(TAG, "Events file not found to persist event to", e);
            throw new EventStoreException("Unable to open events file writer", e);
        } catch (Exception e) {
            Log.e(TAG, "Unexpected exception", e);
            throw new EventStoreException("Unexpected error while creating eventsFile writer", e);
        }
        return writer;
    }

    private void tryCloseWriter(final Writer writer) throws EventStoreException {
        if (writer != null) {
            try {
                writer.close();
            } catch (IOException e) {
                Log.e(TAG, "Unable to close writer for events file", e);
            }
        }
    }

    private File deleteReadEvents(final int lineNumber) {
        // Write all events after line number to a temporary file
        FileManager fileManager = context.getSystem().getFileManager();
        File eventsDir = fileManager.createDirectory(EVENTS_DIRECTORY);

        File tempEventsFile = null;
        try {
            File tempFile = new File(eventsDir, EVENT_FILE_NAME + ".tmp");
            if (tempFile.exists()) {
                if (!tempFile.delete()) {
                    Log.e(TAG, "Failed to delete previous temp file");
                }
            }
            tempEventsFile = fileManager.createFile(tempFile);
        } catch (IOException e) {
            Log.e(TAG,
                    "An error occurred while attempting to create/open the temporary events file",
                    e);
            return eventsFile;
        }

        boolean errorsOccurred = false;
        if (tempEventsFile != null && eventsFile.exists() && tempEventsFile.exists()) {
            BufferedReader reader = null;
            PrintWriter writer = null;
            try {
                reader = new BufferedReader(new FileReader(eventsFile));
                writer = new PrintWriter(new FileWriter(tempEventsFile, true));

                String line = null;
                int currentLineNumber = 0;
                while ((line = reader.readLine()) != null) {
                    currentLineNumber++;
                    if (currentLineNumber > lineNumber) {
                        writer.println(line);
                        writer.flush();
                    }
                }
            } catch (FileNotFoundException e) {
                errorsOccurred = true;
                Log.e(TAG, "The events file count not be found", e);
            } catch (IOException e) {
                Log.e(TAG, "An error occurred while attempting to delete the read events", e);
                errorsOccurred = true;
            } finally {
                if (writer != null) {
                    writer.close();
                }
                if (reader != null) {
                    try {
                        reader.close();
                    } catch (IOException e) {

                    }
                }

                if (!errorsOccurred) {
                    if (eventsFile.delete()) {
                        if (!tempEventsFile.renameTo(eventsFile)) {
                            Log.e(TAG, "Failed to rename temp file to events file");
                        }
                        try {
                            eventsFile = fileManager
                                    .createFile(new File(eventsDir, EVENT_FILE_NAME));
                        } catch (IOException e) {
                            Log.e(TAG,
                                    "An error occurred while attempting to create the eventsFile",
                                    e);
                        }
                    } else {
                        Log.e(TAG, "Failed to delete previous events file");
                    }
                }
            }

            File tempFile = new File(eventsDir, EVENT_FILE_NAME + ".tmp");
            if (tempFile.exists()) {
                if (!tempFile.delete()) {
                    Log.e(TAG, "Failed to delete temp file");
                }
            }
        }
        return eventsFile;
    }

    @Override
    public EventIterator iterator() {
        return new EventIterator() {
            int linesRead = 0;
            String nextBuffer = null;
            BufferedReader reader = null;
            boolean isEndOfFile = false;

            private boolean tryOpenReader() {
                if (reader != null) {
                    return true;
                }

                if (!isEndOfFile) {
                    InputStreamReader streamReader = null;
                    try {
                        streamReader = new InputStreamReader(context.getSystem().getFileManager()
                                .newInputStream(eventsFile), StringUtils.UTF8);
                    } catch (FileNotFoundException e) {
                        Log.e(TAG, "Could not open the events file", e);
                    }

                    if (streamReader != null) {
                        reader = new BufferedReader(streamReader);
                        return true;
                    }
                }
                return false;
            }

            private void tryCloseReader() {
                if (reader != null) {
                    try {
                        reader.close();
                    } catch (IOException ioe) {
                        Log.e(TAG, "Unable to close reader for events file", ioe);
                    } finally {
                        reader = null;
                    }
                }
            }

            @Override
            public boolean hasNext() {
                boolean hasNext = false;
                accessLock.lock();
                try {
                    // If there is something already buffered then there is a
                    // next
                    if (nextBuffer != null) {
                        hasNext = true;
                    } else {
                        if (!tryOpenReader()) {
                            return hasNext;
                        }
                        // Nothing was previously buffered so try to read one
                        // more line
                        boolean found = false;
                        while (!found) {
                            try {
                                nextBuffer = reader.readLine();
                                found = true;
                            } catch (IOException e) {
                                nextBuffer = null;
                                found = true;
                            }
                        }
                        if (nextBuffer != null) {
                            // There was at least one more line so there is a
                            // next
                            hasNext = true;
                        } else {
                            // The next line was null so it should be the end of
                            // the file. Try to close the reader
                            isEndOfFile = true;
                            tryCloseReader();
                        }
                    }
                    return hasNext;
                } finally {
                    accessLock.unlock();
                }
            }

            @Override
            public String next() {
                String next = null;
                accessLock.lock();
                try {
                    if (nextBuffer != null) {
                        next = nextBuffer;
                        linesRead++;
                        nextBuffer = null;
                    } else {
                        if (!tryOpenReader()) {
                            return next;
                        }
                        boolean found = false;
                        while (!found) {
                            try {
                                next = reader.readLine();
                                found = true;
                            } catch (IOException e) {
                                next = null;
                                found = true;
                            }
                        }
                        if (next != null) {
                            linesRead++;
                        } else {
                            isEndOfFile = true;
                            tryCloseReader();
                        }
                    }
                    return next;
                } finally {
                    accessLock.unlock();
                }
            }

            @Override
            public String peek() {
                accessLock.lock();
                try {
                    hasNext();
                    return nextBuffer;
                } finally {
                    accessLock.unlock();
                }
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException(
                        "The remove() operation is not supported for this iterator");
            }

            @Override
            public void removeReadEvents() {
                accessLock.lock();
                try {
                    deleteReadEvents(linesRead);
                    resetReader();
                } finally {
                    accessLock.unlock();
                }
            }

            private void resetReader() {
                tryCloseReader();
                linesRead = 0;
                nextBuffer = null;
            }
        };
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy