Package org.apache.marmotta.platform.core.services.triplestore

Source Code of org.apache.marmotta.platform.core.services.triplestore.SesameServiceImpl$LMFTransactionEventProxy

/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.marmotta.platform.core.services.triplestore;

import edu.emory.mathcs.backport.java.util.concurrent.locks.ReentrantReadWriteLock;
import org.apache.marmotta.commons.sesame.transactions.api.TransactionListener;
import org.apache.marmotta.commons.sesame.transactions.api.TransactionalSail;
import org.apache.marmotta.commons.sesame.transactions.model.TransactionData;
import org.apache.marmotta.commons.sesame.transactions.sail.KiWiTransactionalSail;
import org.apache.marmotta.platform.core.api.config.ConfigurationService;
import org.apache.marmotta.platform.core.api.triplestore.*;
import org.apache.marmotta.platform.core.qualifiers.event.transaction.AfterCommit;
import org.apache.marmotta.platform.core.qualifiers.event.transaction.AfterRollback;
import org.apache.marmotta.platform.core.qualifiers.event.transaction.BeforeCommit;
import org.apache.marmotta.platform.core.qualifiers.inject.Fallback;
import org.apache.marmotta.platform.core.util.CDIContext;
import org.openrdf.model.ValueFactory;
import org.openrdf.repository.RepositoryConnection;
import org.openrdf.repository.RepositoryException;
import org.openrdf.repository.base.RepositoryConnectionWrapper;
import org.openrdf.repository.sail.SailRepository;
import org.openrdf.sail.NotifyingSail;
import org.openrdf.sail.Sail;
import org.openrdf.sail.SailException;
import org.slf4j.Logger;

import javax.annotation.PreDestroy;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Event;
import javax.enterprise.inject.Instance;
import javax.enterprise.inject.Produces;
import javax.enterprise.inject.UnsatisfiedResolutionException;
import javax.inject.Inject;
import java.util.Iterator;

/**
* Offers access to the Sesame repository underlying this Apache Marmotta instance. The activation/deactivation methods
* of this service make sure the repository is properly initialised and shut down.
* <p/>
* Usage: to access the triple store properly through Sesame, you should follow the following
* pattern:
* <pre>
*     RespositoryConnection con = sesameService.getConnection();
*
*     URI subject = con.getValueFactory().createURI(...);
*     ...
*     RepositoryResult&lt;Statement> result = con.getStatemenrs(subject,predicate,object,inferred,context);
*     while(result.hasNext()) {
*         Statement triple = result.next();
*         ...
*     }
*
*     con.close();
* </pre>
*
* <p/>
* Will replace the existing TripleStore at some point.
* <p/>
* Author: Sebastian Schaffert
*/
@ApplicationScoped
public class SesameServiceImpl implements SesameService {

    @Inject
    private Logger log;

    @Inject
    private ConfigurationService configurationService;

    @Inject @BeforeCommit
    private Event<TransactionData> beforeCommitEvent;

    @Inject @AfterCommit
    private Event<TransactionData> afterCommitEvent;

    @Inject @AfterRollback
    private Event<TransactionData> afterRollbackEvent;

    /**
     * triple store providers from backend modules
     */
    @Inject
    private Instance<StoreProvider> storeProviders;

    /**
     * notifying sail providers from other modules
     */
    @Inject
    private Instance<NotifyingSailProvider> notifyingSailProviders;

    /**
     * transactional sail providers from other modules
     */
    @Inject
    private Instance<TransactionalSailProvider> transactionalSailProviders;

    /**
     * normal sail providers from other modules
     */
    @Inject
    private Instance<StandardSailProvider> standardSailProviders;

    /**
     * garbage collectors for backends that support it
     */
    @Inject
    private Instance<GarbageCollectionProvider> garbageCollectionProviders;

    private NotifyingSail store;

    private SailRepository repository;

    private ReentrantReadWriteLock restartLock = new ReentrantReadWriteLock();

    /**
     * Initialise the Sesame repository. Should be called on service activation.
     */
    @Override
    public synchronized void initialise() {
        restartLock.writeLock().lock();
        try {
            log.info("Apache Marmotta Sesame Repository Service starting up ...");

            if(repository != null) {
                log.warn("RDF repository has already been initialized");
            }

            if(storeProviders.isUnsatisfied()) {
                log.error("no storage backend found in classpath; please add one of the marmotta-backend-XXX modules");
                throw new UnsatisfiedResolutionException("no storage backend found in classpath; please add one of the marmotta-backend-XXX modules");
            }

            if(storeProviders.isAmbiguous()) {
                log.warn("more than one storage backend in classpath, trying to select the most appropriate ...");
                StoreProvider candidate = null;

                Iterator<StoreProvider> it = storeProviders.iterator();
                while (it.hasNext()) {
                    StoreProvider next = it.next();

                    log.warn("- candidate: {} (annotations: {})", next.getName(), next.getClass().getAnnotations());

                    if(candidate == null || !next.getClass().isAnnotationPresent(Fallback.class)) {
                        candidate = next;
                    }
                }

                log.warn("selected storage backend: {}", candidate.getName());

                store = candidate.createStore();
            } else {
                store = storeProviders.get().createStore();
            }


            NotifyingSail notifyingSail;

            log.info("initialising repository plugins ...");

            // TODO: should also wrap a transactional sail in case there are observers on the classpath!
            if(   !transactionalSailProviders.isUnsatisfied() || CDIContext.hasObservers(this, "beforeCommitEvent")
               || CDIContext.hasObservers(this, "afterCommitEvent") || CDIContext.hasObservers(this, "afterRollbackEvent")) {
                log.info("enabling transaction notification");

                KiWiTransactionalSail tsail = new KiWiTransactionalSail(store);

                // the CDI events should be triggered once all internal events have been handled, so register the transaction listener last
                tsail.addTransactionListener(new LMFTransactionEventProxy());


                // wrap all stackable transactional sails
                TransactionalSail transactionalSail = tsail;
                for(TransactionalSailProvider provider : transactionalSailProviders) {
                    if(provider.isEnabled()) {
                        log.info("- transaction plugin: {}",provider.getName());
                        transactionalSail = provider.createSail(transactionalSail);
                    } else {
                        log.info("- transaction plugin: {} (DISABLED)", provider.getName());
                    }
                }
                notifyingSail = transactionalSail;
            } else {
                log.info("not enabling transaction notification, because no transaction observers are registered");
                notifyingSail = store;
            }

            // wrap all stackable notifying sails
            for(NotifyingSailProvider provider : notifyingSailProviders) {
                if(provider.isEnabled()) {
                    log.info("- notifying plugin: {}",provider.getName());
                    notifyingSail = provider.createSail(notifyingSail);
                } else {
                    log.info("- notifying plugin: {} (DISABLED)", provider.getName());
                }
            }

            // wrap all standard sails
            Sail standardSail = notifyingSail;
            for(StandardSailProvider provider : standardSailProviders) {
                if(provider.isEnabled()) {
                    log.info("- standard plugin: {}",provider.getName());
                    standardSail = provider.createSail(standardSail);
                } else {
                    log.info("- standard plugin: {} (DISABLED)", provider.getName());
                }
            }


            repository = storeProviders.get().createRepository(standardSail);

            try {
                repository.initialize();
            } catch (RepositoryException e) {
                log.error("error while initialising Apache Marmotta Sesame repository",e);
            }
        } finally {
            restartLock.writeLock().unlock();
        }
    }

    /**
     * Shutdown the Sesame repository. Should be called on service deactivation.
     */
    @Override
    @PreDestroy
    public synchronized void shutdown() {
        restartLock.writeLock().lock();
        try {
            if(repository != null) {
                log.info("Apache Marmotta Sesame Repository Service shutting down ...");
                try {
                    repository.shutDown();
                } catch (RepositoryException e) {
                    log.error("error while shutting down Apache Marmotta Sesame repository",e);
                }
                repository = null;
            }
        } finally {
            restartLock.writeLock().unlock();
        }
    }

    /**
     * Restart the Sesame Service.
     */
    @Override
    public void restart() {
        restartLock.writeLock().lock();
        try {
            shutdown();
            initialise();
        } finally {
            restartLock.writeLock().unlock();
        }
    }

    /**
     * Reinit Sesame repository when the configuration has been initialised
     *
     * @param e
     */
    /*
    public void onConfigurationChange(@Observes ConfigurationChangedEvent e) {
        if(e.containsChangedKeyWithPrefix("database")) {
            shutdown();
            initialise();
        }
    }
    */

    /**
     * Return the Sesame Repository underlying this service. Callers should be careful with modifying
     * this object directly.
     *
     * @return the Sesame Repository instance used by this service
     */
    @Override
    @Produces
    public SailRepository getRepository() {
        return repository;
    }

    /**
     * Return a Sesame RepositoryConnection to the underlying repository. The connection has auto-commit disabled,
     * all transaction management must be performed explicitly.
     *
     * @return a RepositoryConnection to the underlying Sesame repository.
     */
    @Override
    @Produces
    public RepositoryConnection getConnection() throws RepositoryException {
        restartLock.readLock().lock();
        RepositoryConnection connection = repository.getConnection();
        return new RepositoryConnectionWrapper(repository,connection) {
            @Override
            public void close() throws RepositoryException {
                super.close();
                restartLock.readLock().unlock();
            }
        };
    }

    /**
     * Return a Sesame ValueFactory for creating new RDF objects.
     *
     * @return the Sesame ValueFactory belonging to the repository that is used by the service
     */
    @Override
    @Produces
    public ValueFactory getValueFactory() {
        return repository.getValueFactory();
    }

    /**
     * Run the triple store garbage collector manually and clean up unreferenced nodes and triples.
     * @throws SailException
     */
    @Override
    public void garbageCollect() throws SailException {
        if(store != null) {
            for(GarbageCollectionProvider p : garbageCollectionProviders) {
                p.garbageCollect(store);
            }
        }
    }

    private class LMFTransactionEventProxy implements TransactionListener {

        /**
         * Called before a transaction commits. The transaction data will contain all changes done in the transaction since
         * the last commit. This method should be used in case the transaction listener aims to perform additional activities
         * in the same transaction, like inserting or updating database tables.
         * <p/>
         * The implementation in lmf-core simply wraps the KiWi transaction data in a CDI transaction to notify other
         * services in the system.
         *
         * @param data
         */
        @Override
        public void beforeCommit(TransactionData data) {
            log.debug("transaction: before commit event");
            beforeCommitEvent.fire(data);
        }

        /**
         * Called after a transaction has committed. The transaction data will contain all changes done in the transaction since
         * the last commit. This method should be used in case the transaction listener aims to perform additional activities
         * in a new transaction or outside the transaction management, e.g. notifying a server on the network, adding
         * data to a cache, or similar.
         * <p/>
         * The implementation in lmf-core simply wraps the KiWi transaction data in a CDI transaction to notify other
         * services in the system.
         *
         * @param data
         */
        @Override
        public void afterCommit(TransactionData data) {
            log.debug("transaction: after commit event");
            afterCommitEvent.fire(data);
        }

        /**
         * Called when a transaction rolls back.
         * <p/>
         * The implementation in lmf-core simply wraps the KiWi transaction data in a CDI transaction to notify other
         * services in the system.
         */
        @Override
        public void rollback(TransactionData data) {
            log.debug("transaction: rollback event");
            afterRollbackEvent.fire(data);
        }

    }

}
TOP

Related Classes of org.apache.marmotta.platform.core.services.triplestore.SesameServiceImpl$LMFTransactionEventProxy

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.