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.FedoraRepository;
030import org.fcrepo.kernel.api.exception.RepositoryRuntimeException;
031import org.fcrepo.kernel.modeshape.FedoraRepositoryImpl;
032import org.modeshape.jcr.JcrRepository;
033import org.modeshape.jcr.ModeShapeEngine;
034import org.modeshape.jcr.NoSuchRepositoryException;
035import org.modeshape.jcr.RepositoryConfiguration;
036import org.slf4j.Logger;
037import org.springframework.beans.factory.FactoryBean;
038import org.springframework.core.io.Resource;
039
040/**
041 * A Modeshape factory shim to make it play nice with our Spring-based
042 * configuration
043 *
044 * @author Edwin Shin
045 * @since Feb 7, 2013
046 */
047public class ModeShapeRepositoryFactoryBean implements
048        FactoryBean<FedoraRepository> {
049
050    private static final Logger LOGGER =
051            getLogger(ModeShapeRepositoryFactoryBean.class);
052
053    private DefaultPropertiesLoader propertiesLoader;
054
055    @Inject
056    private ModeShapeEngine modeShapeEngine;
057
058    private Resource repositoryConfiguration;
059
060    private JcrRepository repository;
061
062    /**
063     * Generate a JCR repository from the given configuration
064     *
065     */
066    @PostConstruct
067    public void buildRepository() {
068        try {
069            LOGGER.info("Using repo config (classpath): {}", repositoryConfiguration.getURL());
070            getPropertiesLoader().loadSystemProperties();
071
072            final RepositoryConfiguration config =
073                    RepositoryConfiguration.read(repositoryConfiguration.getURL());
074            repository = modeShapeEngine.deploy(config);
075
076            // next line ensures that repository starts before the factory is used.
077            final org.modeshape.common.collection.Problems problems =
078                    repository.getStartupProblems();
079            for (final org.modeshape.common.collection.Problem p : problems) {
080                LOGGER.error("ModeShape Start Problem: {}", p.getMessageString());
081                // TODO determine problems that should be runtime errors
082            }
083        } catch (final Exception e) {
084            throw new RepositoryRuntimeException(e);
085        }
086    }
087
088    /**
089     * Attempts to undeploy the repository and shutdown the ModeShape engine on
090     * context destroy.
091     *
092     * @throws InterruptedException if interrupted exception occurred
093     */
094    @PreDestroy
095    public void stopRepository() throws InterruptedException {
096        LOGGER.info("Initiating shutdown of ModeShape");
097        final String repoName = repository.getName();
098        try {
099            final Future<Boolean> futureUndeployRepo = modeShapeEngine.undeploy(repoName);
100            if (futureUndeployRepo.get()) {
101                LOGGER.info("ModeShape repository {} has undeployed.", repoName);
102            } else {
103                LOGGER.error("ModeShape repository {} undeploy failed without an exception, still deployed.", repoName);
104            }
105            LOGGER.info("Repository {} undeployed.", repoName);
106        } catch (final NoSuchRepositoryException e) {
107            LOGGER.error("Repository {} unknown, cannot undeploy.", repoName, e);
108        } catch (final ExecutionException e) {
109            LOGGER.error("Repository {} cannot undeploy.", repoName, e.getCause());
110        }
111        final Future<Boolean> futureShutdownEngine = modeShapeEngine.shutdown();
112        try {
113            if (futureShutdownEngine.get()) {
114                LOGGER.info("ModeShape Engine has shutdown.");
115            } else {
116                LOGGER.error("ModeShape Engine shutdown failed without an exception, still running.");
117            }
118        } catch (final ExecutionException e) {
119            LOGGER.error("ModeShape Engine shutdown failed.", e.getCause());
120        }
121    }
122
123    @Override
124    public FedoraRepository getObject() {
125        return new FedoraRepositoryImpl(repository);
126    }
127
128    @Override
129    public Class<?> getObjectType() {
130        return FedoraRepository.class;
131    }
132
133    @Override
134    public boolean isSingleton() {
135        return true;
136    }
137
138    /**
139     * Set the configuration to use for creating the repository
140     *
141     * @param repositoryConfiguration the repository configuration
142     */
143    public void setRepositoryConfiguration(
144            final Resource repositoryConfiguration) {
145        this.repositoryConfiguration = repositoryConfiguration;
146    }
147
148    private DefaultPropertiesLoader getPropertiesLoader() {
149        if (null == propertiesLoader) {
150            propertiesLoader = new DefaultPropertiesLoader();
151        }
152        return propertiesLoader;
153    }
154}