001/*
002 * Licensed to DuraSpace under one or more contributor license agreements.
003 * See the NOTICE file distributed with this work for additional information
004 * regarding copyright ownership.
005 *
006 * DuraSpace licenses this file to you under the Apache License,
007 * Version 2.0 (the "License"); you may not use this file except in
008 * compliance with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.fcrepo.kernel.modeshape.spring;
019
020import static org.slf4j.LoggerFactory.getLogger;
021
022import java.util.concurrent.ExecutionException;
023import java.util.concurrent.Future;
024
025import javax.annotation.PostConstruct;
026import javax.annotation.PreDestroy;
027import javax.inject.Inject;
028
029import org.fcrepo.kernel.api.exception.RepositoryRuntimeException;
030import org.modeshape.jcr.JcrRepository;
031import org.modeshape.jcr.ModeShapeEngine;
032import org.modeshape.jcr.NoSuchRepositoryException;
033import org.modeshape.jcr.RepositoryConfiguration;
034import org.slf4j.Logger;
035import org.springframework.beans.factory.FactoryBean;
036import org.springframework.core.io.Resource;
037
038/**
039 * A Modeshape factory shim to make it play nice with our Spring-based
040 * configuration
041 *
042 * @author Edwin Shin
043 * @since Feb 7, 2013
044 */
045public class ModeShapeRepositoryFactoryBean implements
046        FactoryBean<JcrRepository> {
047
048    private static final Logger LOGGER =
049            getLogger(ModeShapeRepositoryFactoryBean.class);
050
051    private DefaultPropertiesLoader propertiesLoader;
052
053    @Inject
054    private ModeShapeEngine modeShapeEngine;
055
056    private Resource repositoryConfiguration;
057
058    private JcrRepository repository;
059
060    /**
061     * Generate a JCR repository from the given configuration
062     *
063     */
064    @PostConstruct
065    public void buildRepository() {
066        try {
067            LOGGER.info("Using repo config (classpath): {}", repositoryConfiguration.getURL());
068            getPropertiesLoader().loadSystemProperties();
069
070            final RepositoryConfiguration config =
071                    RepositoryConfiguration.read(repositoryConfiguration.getURL());
072            repository = modeShapeEngine.deploy(config);
073
074            // next line ensures that repository starts before the factory is used.
075            final org.modeshape.common.collection.Problems problems =
076                    repository.getStartupProblems();
077            for (final org.modeshape.common.collection.Problem p : problems) {
078                LOGGER.error("ModeShape Start Problem: {}", p.getMessageString());
079                // TODO determine problems that should be runtime errors
080            }
081        } catch (final Exception e) {
082            throw new RepositoryRuntimeException(e);
083        }
084    }
085
086    /**
087     * Attempts to undeploy the repository and shutdown the ModeShape engine on
088     * context destroy.
089     *
090     * @throws InterruptedException if interrupted exception occurred
091     */
092    @PreDestroy
093    public void stopRepository() throws InterruptedException {
094        LOGGER.info("Initiating shutdown of ModeShape");
095        final String repoName = repository.getName();
096        try {
097            final Future<Boolean> futureUndeployRepo = modeShapeEngine.undeploy(repoName);
098            if (futureUndeployRepo.get()) {
099                LOGGER.info("ModeShape repository {} has undeployed.", repoName);
100            } else {
101                LOGGER.error("ModeShape repository {} undeploy failed without an exception, still deployed.", repoName);
102            }
103            LOGGER.info("Repository {} undeployed.", repoName);
104        } catch (final NoSuchRepositoryException e) {
105            LOGGER.error("Repository {} unknown, cannot undeploy.", repoName, e);
106        } catch (final ExecutionException e) {
107            LOGGER.error("Repository {} cannot undeploy.", repoName, e.getCause());
108        }
109        final Future<Boolean> futureShutdownEngine = modeShapeEngine.shutdown();
110        try {
111            if (futureShutdownEngine.get()) {
112                LOGGER.info("ModeShape Engine has shutdown.");
113            } else {
114                LOGGER.error("ModeShape Engine shutdown failed without an exception, still running.");
115            }
116        } catch (final ExecutionException e) {
117            LOGGER.error("ModeShape Engine shutdown failed.", e.getCause());
118        }
119    }
120
121    @Override
122    public JcrRepository getObject() {
123        return repository;
124    }
125
126    @Override
127    public Class<?> getObjectType() {
128        return JcrRepository.class;
129    }
130
131    @Override
132    public boolean isSingleton() {
133        return true;
134    }
135
136    /**
137     * Set the configuration to use for creating the repository
138     *
139     * @param repositoryConfiguration the repository configuration
140     */
141    public void setRepositoryConfiguration(
142            final Resource repositoryConfiguration) {
143        this.repositoryConfiguration = repositoryConfiguration;
144    }
145
146    private DefaultPropertiesLoader getPropertiesLoader() {
147        if (null == propertiesLoader) {
148            propertiesLoader = new DefaultPropertiesLoader();
149        }
150        return propertiesLoader;
151    }
152}