Package com.lowtuna.jsonblob.core

Source Code of com.lowtuna.jsonblob.core.BlobManager$ClosableLock

package com.lowtuna.jsonblob.core;

import com.codahale.metrics.CachedGauge;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Timer;
import com.google.common.collect.Maps;
import com.mongodb.BasicDBObject;
import com.mongodb.BasicDBObjectBuilder;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.mongodb.WriteResult;
import com.mongodb.util.JSON;
import com.mongodb.util.JSONParseException;
import io.dropwizard.lifecycle.Managed;
import io.dropwizard.util.Duration;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.bson.types.ObjectId;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;

import javax.annotation.concurrent.GuardedBy;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

@Slf4j
public class BlobManager implements Managed, Runnable {
    public static final String UPDATED_ATTR_NAME = "updated";
    public static final String CREATED_ATTR_NAME = "created";
    public static final String ACCESSED_ATTR_NAME = "accessed";

    @GuardedBy("pendingLastAccessedWritesLock")
    private final Map<ObjectId, DateTime> pendingLastAccessedWrites = Maps.newHashMap();
    private final ReentrantReadWriteLock pendingLastAccessedWritesLock = new ReentrantReadWriteLock();

    private final ScheduledExecutorService scheduledExecutorService;
    private final Duration blobCleanupFrequency;
    @Getter
    private final Duration blobAccessTtl;
    private final MetricRegistry metricRegistry;
    @Getter
    private final boolean deleteEnabled;

    private final DBCollection collection;
    private final Timer createTimer;
    private final Timer readTimer;
    private final Timer updateTimer;
    private final Timer deleteTimer;

    public BlobManager(DB mongoDb, String blobCollectionName, ScheduledExecutorService scheduledExecutorService, Duration blobCleanupFrequency, Duration blobAccessTtl, MetricRegistry metrics, boolean deleteEnabled) {
        this.scheduledExecutorService = scheduledExecutorService;
        this.blobCleanupFrequency = blobCleanupFrequency;
        this.blobAccessTtl = blobAccessTtl;
        this.metricRegistry = metrics;
        this.deleteEnabled = deleteEnabled;

        this.collection = mongoDb.getCollection(blobCollectionName);
        this.createTimer = metrics.timer(MetricRegistry.name(getClass(), "create"));
        this.readTimer = metrics.timer(MetricRegistry.name(getClass(), "read"));
        this.updateTimer = metrics.timer(MetricRegistry.name(getClass(), "update"));
        this.deleteTimer = metrics.timer(MetricRegistry.name(getClass(), "delete"));

        metrics.register(MetricRegistry.name(getClass(), "blobCount"), new CachedGauge<Long>(1, TimeUnit.HOURS) {
            @Override
            protected Long loadValue() {
                return collection.count();
            }
        });

        log.info("Blob deletion is {}", deleteEnabled ? "enabled" : "disabled");
    }

    private BasicDBObject getDBObject(ObjectId objectId) {
        return new BasicDBObject("_id", objectId);
    }

    private DBObject createDBObject(String json, boolean setCreated) {
        final DateTime now = DateTime.now(DateTimeZone.UTC);
        BasicDBObjectBuilder builder = BasicDBObjectBuilder
                .start(UPDATED_ATTR_NAME, new Date(now.getMillis()))
                .append(ACCESSED_ATTR_NAME, new Date(now.getMillis()))
                .append("blob", JSON.parse(json));

        if (setCreated) {
            builder = builder.append(CREATED_ATTR_NAME, new Date(now.getMillis()));
        }

        return builder.get();
    }

    public static boolean isValidJson(String json) {
        try {
            JSON.parse(json);
            return true;
        } catch (JSONParseException e) {
            return false;
        }
    }

    public DBObject create(String json) {
        try (Timer.Context timerContext = createTimer.time()) {
            log.debug("inserting blob");
            log.trace("new blob json='{}'", json);
            DBObject parsed = createDBObject(json, true);
            collection.insert(parsed);
            log.debug("successfully inserted blob of json as objectId='{}'", parsed.get("_id"));
            return parsed;
        }
    }

    public DBObject read(final ObjectId id) throws BlobNotFoundException {
        try (Timer.Context timerContext = readTimer.time()) {
            log.debug("attempting to retrieve blob with id='{}'", id);
            DBObject objectId = getDBObject(id);
            if (objectId != null) {
                log.debug("finding blob with objectId='{}'", objectId);
                final DBObject obj = collection.findOne(objectId);
                if (obj != null) {
                    try(ClosableLock closableLock = new ClosableLock(pendingLastAccessedWritesLock.writeLock())) {
                        closableLock.lock();
                        pendingLastAccessedWrites.put(id, DateTime.now(DateTimeZone.UTC));
                    }

                    return obj;
                }
            }
            log.debug("couldn't retrieve blob with id='{}'", id);
            throw new BlobNotFoundException(id);
        }
    }

    public DBObject update(ObjectId id, String json) throws BlobNotFoundException {
        try (Timer.Context timerContext = updateTimer.time()) {
            log.debug("attempting to update blob with id='{}'", id);
            log.trace("blob json='{}'", json);
            DBObject objectId = getDBObject(id);
            if (objectId != null) {
                log.debug("finding blob to update with objectId='{}'", objectId);
                DBObject obj = collection.findOne(objectId);
                if (obj != null) {
                    DBObject parsed = createDBObject(json, false);
                    collection.update(obj, parsed);
                    log.debug("successfully updated blob of json with objectId='{}'", id);
                    return parsed;
                }
            }
            log.debug("couldn't update blob with id='{}'", id);
            throw new BlobNotFoundException(id);
        }
    }

    public boolean delete(ObjectId id) throws BlobNotFoundException {
        try (Timer.Context timerContext = deleteTimer.time()) {
            log.debug("attempting to delete blob with id='{}'", id);
            DBObject objectId = getDBObject(id);
            if (objectId != null) {
                log.debug("finding blob to delete with objectId='{}'", objectId);
                DBObject obj = collection.findOne(objectId);
                if (obj != null) {
                    WriteResult result = collection.remove(obj);
                    boolean removed = result.getN() > 0 && result.getLastError().ok();
                    if (removed) {
                        log.debug("successfully removed {} blob(s) of json with objectId='{}'", result.getN(), id);
                    } else {
                        log.debug("did not remove any blob(s) of json with objectId='{}'", id);
                    }
                    return removed;
                }
            }
            log.debug("couldn't remove blob with id='{}'", id);
            throw new BlobNotFoundException(id);
        }
    }

    @Override
    public void start() throws Exception {
        if (deleteEnabled) {
            scheduledExecutorService.scheduleWithFixedDelay(
                    new BlobCleanupJob(collection, blobAccessTtl, metricRegistry),
                    0,
                    blobCleanupFrequency.getQuantity(),
                    blobCleanupFrequency.getUnit()
            );
        }
        scheduledExecutorService.scheduleWithFixedDelay(this, 1, 1, TimeUnit.MINUTES);
    }

    @Override
    public void stop() throws Exception {
        run();
    }

    @Override
    public void run() {
        HashMap<ObjectId, DateTime> updates = Maps.newHashMap();

        try(ClosableLock closableLock = new ClosableLock(pendingLastAccessedWritesLock.writeLock())) {
            closableLock.lock();
            updates.putAll(pendingLastAccessedWrites);
            pendingLastAccessedWrites.clear();
        }

        log.debug("updating last accessed time for {} blobs", updates.size());
        for (Map.Entry<ObjectId, DateTime> lastAccessedEntry: updates.entrySet()) {
            final DBObject obj = collection.findOne(lastAccessedEntry.getKey());
            if (obj != null) {
                DateTime accessed = lastAccessedEntry.getValue();

                BasicDBObject updatedAccessedDbObject = new BasicDBObject();
                updatedAccessedDbObject.append("$set", new BasicDBObject().append(ACCESSED_ATTR_NAME, new Date(accessed.getMillis())));

                log.debug("updating last accessed time for blob with objectId='{}' to {}", lastAccessedEntry.getKey(), accessed);
                collection.update(obj, updatedAccessedDbObject, false, false);
                log.debug("updated last accessed time for blob with objectId='{}' to {}", lastAccessedEntry.getKey(), accessed);
            }
        }
    }

    @RequiredArgsConstructor
    private static class ClosableLock implements AutoCloseable {
        private final Lock lock;

        public void lock() {
            lock.lock();
        }

        public void close() {
            lock.unlock();
        }
    }
}
TOP

Related Classes of com.lowtuna.jsonblob.core.BlobManager$ClosableLock

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.