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
040import static org.fcrepo.kernel.api.RdfLexicon.SERVER_MANAGED_PROPERTIES_MODE;
041
042/**
043 * A Modeshape factory shim to make it play nice with our Spring-based
044 * configuration
045 *
046 * @author Edwin Shin
047 * @since Feb 7, 2013
048 */
049public class ModeShapeRepositoryFactoryBean implements
050        FactoryBean<FedoraRepository> {
051
052    private static final Logger LOGGER =
053            getLogger(ModeShapeRepositoryFactoryBean.class);
054
055    private DefaultPropertiesLoader propertiesLoader;
056
057    @Inject
058    private ModeShapeEngine modeShapeEngine;
059
060    private Resource repositoryConfiguration;
061
062    private JcrRepository repository;
063
064    /**
065     * Generate a JCR repository from the given configuration
066     *
067     */
068    @PostConstruct
069    public void buildRepository() {
070        try {
071            LOGGER.info("Using repo config (classpath): {}", repositoryConfiguration.getURL());
072            getPropertiesLoader().loadSystemProperties();
073
074            final RepositoryConfiguration config =
075                    RepositoryConfiguration.read(repositoryConfiguration.getURL());
076            repository = modeShapeEngine.deploy(config);
077
078            // next line ensures that repository starts before the factory is used.
079            final org.modeshape.common.collection.Problems problems =
080                    repository.getStartupProblems();
081            for (final org.modeshape.common.collection.Problem p : problems) {
082                LOGGER.error("ModeShape Start Problem: {}", p.getMessageString());
083                // TODO determine problems that should be runtime errors
084            }
085        } catch (final Exception e) {
086            throw new RepositoryRuntimeException(e);
087        }
088    }
089
090    /**
091     * Attempts to undeploy the repository and shutdown the ModeShape engine on
092     * context destroy.
093     *
094     * @throws InterruptedException if interrupted exception occurred
095     */
096    @PreDestroy
097    public void stopRepository() throws InterruptedException {
098        LOGGER.info("Initiating shutdown of ModeShape");
099        final String repoName = repository.getName();
100        try {
101            final Future<Boolean> futureUndeployRepo = modeShapeEngine.undeploy(repoName);
102            if (futureUndeployRepo.get()) {
103                LOGGER.info("ModeShape repository {} has undeployed.", repoName);
104            } else {
105                LOGGER.error("ModeShape repository {} undeploy failed without an exception, still deployed.", repoName);
106            }
107            LOGGER.info("Repository {} undeployed.", repoName);
108        } catch (final NoSuchRepositoryException e) {
109            LOGGER.error("Repository {} unknown, cannot undeploy.", repoName, e);
110        } catch (final ExecutionException e) {
111            LOGGER.error("Repository {} cannot undeploy.", repoName, e.getCause());
112        }
113        final Future<Boolean> futureShutdownEngine = modeShapeEngine.shutdown();
114        try {
115            if (futureShutdownEngine.get()) {
116                LOGGER.info("ModeShape Engine has shutdown.");
117            } else {
118                LOGGER.error("ModeShape Engine shutdown failed without an exception, still running.");
119            }
120        } catch (final ExecutionException e) {
121            LOGGER.error("ModeShape Engine shutdown failed.", e.getCause());
122        }
123    }
124
125    @Override
126    public FedoraRepository getObject() {
127        return new FedoraRepositoryImpl(repository);
128    }
129
130    @Override
131    public Class<?> getObjectType() {
132        return FedoraRepository.class;
133    }
134
135    @Override
136    public boolean isSingleton() {
137        return true;
138    }
139
140    /**
141     * Set the configuration to use for creating the repository
142     *
143     * @param repositoryConfiguration the repository configuration
144     */
145    public void setRepositoryConfiguration(
146            final Resource repositoryConfiguration) {
147        this.repositoryConfiguration = repositoryConfiguration;
148    }
149
150    private DefaultPropertiesLoader getPropertiesLoader() {
151        if (null == propertiesLoader) {
152            propertiesLoader = new DefaultPropertiesLoader();
153        }
154        return propertiesLoader;
155    }
156
157    /**
158     * Set the mode for server managed properties.  The value "strict" (default)
159     * will result in the classic behavior where certain provenance-related
160     * properties cannot be set, while "relaxed" will allow for them to be
161     * set in some circumstances.
162     *
163     * @param value the value (must be either "strict" or "relaxed")
164     */
165    public void setServerManagedPropertiesMode(final String value) {
166        if ("strict".equals(value) || "relaxed".equals(value)) {
167            final String propertyValue = System.getProperty(SERVER_MANAGED_PROPERTIES_MODE);
168            if (propertyValue != null) {
169                LOGGER.warn("The system property \"{}\" with the value \"{}\" is being used instead of the" +
170                                "serverManagedPropertiesMode of \"{}\" specified in the Spring configuration!",
171                        SERVER_MANAGED_PROPERTIES_MODE, propertyValue, value);
172            } else {
173                LOGGER.info("Set sytem property \"{}\" to \"{}\" to correspond to serverManagedPropertiesMode.",
174                        SERVER_MANAGED_PROPERTIES_MODE, value);
175                System.setProperty(SERVER_MANAGED_PROPERTIES_MODE, value);
176            }
177        } else {
178            throw new IllegalArgumentException("Invalid value \"" + value
179                    + "\" supplied for serverManagedPropertiesMode (expecting \"strict\" or \"relaxed\")!");
180        }
181    }
182}