at.newmedialab.ldclient.service.LDCache.java Source code

Java tutorial

Introduction

Here is the source code for at.newmedialab.ldclient.service.LDCache.java

Source

/*
 * Copyright (c) 2011 Salzburg Research.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License 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 at.newmedialab.ldclient.service;

import at.newmedialab.ldclient.api.LDCacheProvider;
import at.newmedialab.ldclient.exception.LDClientException;
import at.newmedialab.ldclient.model.CacheEntry;
import at.newmedialab.ldclient.model.ClientResponse;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.openrdf.model.Statement;
import org.openrdf.model.URI;
import org.openrdf.repository.RepositoryConnection;
import org.openrdf.repository.RepositoryException;
import org.openrdf.repository.RepositoryResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Date;
import java.util.HashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Add file description here!
 * <p/>
 * Author: Sebastian Schaffert
 */
public class LDCache {

    private static final String CTX_CACHE = "http://www.newmedialab.at/ldclient/cache";

    private Logger log = LoggerFactory.getLogger(LDCache.class);

    private LDClient clientService;

    private LDEndpoints endpointService;

    private ThreadLocal<Boolean> inProgress = new ThreadLocal<Boolean>();

    private LDCacheProvider cacheProvider;

    // lock a resource while refreshing it so that not several threads trigger a refresh at the same time
    private HashMap<URI, Lock> resourceLocks;

    private Configuration config;

    public LDCache(LDCacheProvider ldCacheProvider) {
        log.info("Linked Data Caching Service initialising ...");
        try {
            config = new PropertiesConfiguration("ldclient.properties");
        } catch (ConfigurationException e) {
            log.warn(
                    "could not load configuration file ldclient.properties from current directory, home directory, or classpath");
        }

        resourceLocks = new HashMap<URI, Lock>();

        cacheProvider = ldCacheProvider;
        clientService = new LDClient();
        endpointService = new LDEndpoints();
    }

    private void lockResource(URI resource) {
        Lock lock;
        synchronized (resourceLocks) {
            lock = resourceLocks.get(resource);
            if (lock == null) {
                lock = new ReentrantLock();
                resourceLocks.put(resource, lock);
            }
        }
        lock.lock();
    }

    private void unlockResource(URI resource) {
        Lock lock;
        synchronized (resourceLocks) {
            lock = resourceLocks.remove(resource);
        }
        if (lock != null) {
            lock.unlock();
        }
    }

    /**
     * Refresh the cached resource passed as argument. The method will do nothing for local resources.
     * Calling the method will carry out the following tasks:
     * 1. check whether the resource is a remote resource; if no, returns immediately
     * 2. check whether the resource has a cache entry; if no, goto 4
     * 3. check whether the expiry time of the cache entry has passed; if no, returns immediately
     * 4. retrieve the triples for the resource from the Linked Data Cloud using the methods offered by the
     * LinkedDataClientService (registered endpoints etc); returns immediately if the result is null or
     * an exception is thrown
     * 5. remove all old triples for the resource and add all new triples for the resource
     * 6. create new expiry information of the cache entry and persist it in the transaction
     *
     * @param resource
     */
    public void refreshResource(URI resource) {

        lockResource(resource);
        try {

            // 2. check whether the resource has a cache entry; if no, goto 4
            CacheEntry entry = getCacheEntry(resource);

            // 3. check whether the expiry time of the cache entry has passed; if no, returns immediately
            if (entry != null && entry.getExpiryDate().after(new Date())) {
                log.info("not refreshing resource {}, as the cached entry is not yet expired", resource);
                return;
            }

            // 4.
            log.debug("refreshing resource {}", resource);
            try {
                ClientResponse response = clientService.retrieveResource(resource);

                if (response != null) {
                    log.info("refreshed resource {}", resource);

                    URI context = cacheProvider.getTripleRepository().getValueFactory().createURI(CTX_CACHE);

                    RepositoryConnection lmfConnection = cacheProvider.getTripleRepository().getConnection();
                    RepositoryConnection respConnection = response.getTriples().getConnection();

                    lmfConnection.remove(resource, null, null, context);

                    RepositoryResult<Statement> triples = respConnection.getStatements(null, null, null, true);
                    while (triples.hasNext()) {
                        Statement triple = triples.next();
                        try {
                            lmfConnection.add(triple, context);
                        } catch (RuntimeException ex) {
                            log.warn("not adding triple {}: an exception occurred ({})", triple, ex.getMessage());
                        }
                    }
                    lmfConnection.commit();

                    lmfConnection.close();
                    respConnection.close();

                    CacheEntry newEntry = new CacheEntry();
                    newEntry.setResource(resource);
                    newEntry.setExpiryDate(response.getExpires());
                    newEntry.setLastRetrieved(new Date());
                    if (entry != null) {
                        newEntry.setUpdateCount(entry.getUpdateCount() + 1);
                    } else {
                        newEntry.setUpdateCount((long) 1);
                    }

                    cacheProvider.getMetadataRepository().put(resource.stringValue(), newEntry);

                }

            } catch (LDClientException e) {
                // on exception, save an expiry information and retry in one day
                CacheEntry newEntry = new CacheEntry();
                newEntry.setResource(resource);
                newEntry.setExpiryDate(
                        new Date(System.currentTimeMillis() + config.getInt("expiry", 86400) * 1000));
                newEntry.setLastRetrieved(new Date());
                if (entry != null) {
                    newEntry.setUpdateCount(entry.getUpdateCount() + 1);
                } else {
                    newEntry.setUpdateCount((long) 1);
                }

                cacheProvider.getMetadataRepository().put(resource.stringValue(), newEntry);

                log.error("refreshing the remote resource {} from the Linked Data Cloud failed ({})", resource,
                        e.getMessage());
                return;
            } catch (RepositoryException e) {
                log.error("repository error while refreshing the remote resource {} from the Linked Data Cloud",
                        resource, e);
                return;
            }
        } finally {
            unlockResource(resource);
        }

    }

    private CacheEntry getCacheEntry(URI resource) {
        return cacheProvider.getMetadataRepository().get(resource.stringValue());
    }

    public LDEndpoints getEndpointService() {
        return endpointService;
    }

}