001package fr.ifremer.adagio.core.service;
002
003/*
004 * #%L
005 * SIH-Adagio Core Shared
006 * $Id: ServiceLocator.java 12053 2014-04-22 09:34:15Z tc1fbb1 $
007 * $HeadURL: https://forge.ifremer.fr/svn/sih-adagio/tags/adagio-3.5.6/core-shared/src/main/java/fr/ifremer/adagio/core/service/ServiceLocator.java $
008 * %%
009 * Copyright (C) 2012 - 2013 Ifremer
010 * %%
011 * This program is free software: you can redistribute it and/or modify
012 * it under the terms of the GNU Affero General Public License as published by
013 * the Free Software Foundation, either version 3 of the License, or
014 * (at your option) any later version.
015 * 
016 * This program is distributed in the hope that it will be useful,
017 * but WITHOUT ANY WARRANTY; without even the implied warranty of
018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
019 * GNU General Public License for more details.
020 * 
021 * You should have received a copy of the GNU Affero General Public License
022 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
023 * #L%
024 */
025
026import java.io.Closeable;
027import java.io.IOException;
028
029import org.apache.commons.logging.Log;
030import org.apache.commons.logging.LogFactory;
031import org.springframework.beans.factory.access.BeanFactoryLocator;
032import org.springframework.beans.factory.access.BeanFactoryReference;
033import org.springframework.context.ApplicationContext;
034import org.springframework.context.access.ContextSingletonBeanFactoryLocator;
035import org.springframework.context.support.AbstractApplicationContext;
036
037import fr.ifremer.adagio.core.service.referential.location.LocationService;
038import fr.ifremer.adagio.core.service.technical.CacheService;
039
040/**
041 * Locates and provides all available application services.
042 */
043public class ServiceLocator implements Closeable {
044
045    /* Logger */
046    private static final Log log = LogFactory.getLog(ServiceLocator.class);
047
048    /**
049     * The default bean reference factory location.
050     */
051    private static final String DEFAULT_BEAN_REFERENCE_LOCATION = "beanRefFactory.xml";
052
053    /**
054     * The default bean reference factory ID.
055     */
056    private static final String DEFAULT_BEAN_REFERENCE_ID = "beanRefFactory";
057
058    /**
059     * The shared instance of this ServiceLocator.
060     */
061    private static ServiceLocator instance = new ServiceLocator();
062
063    /**
064     * Indicates if the spring context is open or not.
065     */
066    private boolean open = false;
067
068    protected ServiceLocator() {
069        // shouldn't be instantiated
070        init(null, null);
071    }
072
073    protected ServiceLocator(String beanFactoryReferenceLocation,
074                             String beanRefFactoryReferenceId) {
075        init(beanFactoryReferenceLocation, beanRefFactoryReferenceId);
076    }
077
078    /**
079     * replace the default shared instance of this Class
080     *
081     * @param newInstance the new shared service locator instance.
082     */
083    public static void setInstance(ServiceLocator newInstance) {
084        instance = newInstance;
085    }
086
087    /**
088     * Gets the shared instance of this Class
089     *
090     * @return the shared service locator instance.
091     */
092    public static ServiceLocator instance() {
093        return instance;
094    }
095
096    /**
097     * The bean factory reference instance.
098     */
099    private BeanFactoryReference beanFactoryReference;
100
101    /**
102     * The bean factory reference location.
103     */
104    private String beanFactoryReferenceLocation;
105
106    /**
107     * The bean factory reference id.
108     */
109    private String beanRefFactoryReferenceId;
110
111    /**
112     * Initializes the Spring application context from the given <code>beanFactoryReferenceLocation</code>. If <code>null</code> is
113     * specified for the <code>beanFactoryReferenceLocation</code> then the
114     * default application context will be used.
115     *
116     * @param beanFactoryReferenceLocation the location of the beanRefFactory reference.
117     */
118    public synchronized void init(String beanFactoryReferenceLocation,
119                                  String beanRefFactoryReferenceId) {
120        // Log if default values are overrided
121        if (log.isDebugEnabled() && beanFactoryReferenceLocation != null && beanRefFactoryReferenceId != null) {
122            log.debug(String.format("Initializing ServiceLocator to use Spring bean factory [%s] at: %s", beanRefFactoryReferenceId,
123                    beanFactoryReferenceLocation));
124        }
125
126        this.beanFactoryReferenceLocation =
127                beanFactoryReferenceLocation == null ?
128                DEFAULT_BEAN_REFERENCE_LOCATION :
129                beanFactoryReferenceLocation;
130        this.beanRefFactoryReferenceId = beanRefFactoryReferenceId == null ?
131                                         DEFAULT_BEAN_REFERENCE_ID :
132                                         beanRefFactoryReferenceId;
133        this.beanFactoryReference = null;
134    }
135
136    /**
137     * Initializes the Spring application context from the given <code>beanFactoryReferenceLocation</code>. If <code>null</code> is
138     * specified for the <code>beanFactoryReferenceLocation</code> then the
139     * default application context will be used.
140     *
141     * @param beanFactoryReferenceLocation the location of the beanRefFactory reference.
142     */
143    public synchronized void init(String beanFactoryReferenceLocation) {
144        this.beanFactoryReferenceLocation = beanFactoryReferenceLocation == null ?
145                                            DEFAULT_BEAN_REFERENCE_LOCATION :
146                                            beanFactoryReferenceLocation;
147        this.beanFactoryReference = null;
148    }
149
150    /**
151     * Shuts down the ServiceLocator and releases any used resources.
152     */
153    public synchronized void shutdown() {
154        // Do not try to close if not already opened
155        if (!open) {
156            return;
157        }
158        if (log.isDebugEnabled()) {
159            log.debug("Close Spring application context");
160        }
161        
162        ((AbstractApplicationContext) getContext()).close();
163        if (beanFactoryReference != null) {
164            beanFactoryReference.release();
165            beanFactoryReference = null;
166        }
167        open = false;
168    }
169
170    /**
171     * Get a service.
172     *
173     * @param name        name of the service (i.e name of the srping bean)
174     * @param serviceType type of service
175     * @param <S>         type of the service
176     * @return the instanciated service
177     */
178    public <S> S getService(String name, Class<S> serviceType) {
179        return getContext().getBean(name, serviceType);
180    }
181
182    /**
183     * Gets an instance of {@link LocationService}.
184     */
185    public final LocationService getLocationService() {
186        return getService("locationService", LocationService.class);
187    }
188 
189
190    /**
191     * Gets an instance of {@link CacheService}.
192     */
193    public final CacheService getCacheService() {
194        return getService("cacheService", CacheService.class);
195    }
196
197    /**
198     * @return {@code true} if spring context is open, {@code false} otherwise.
199     * @since 3.5.2
200     */
201    public boolean isOpen() {
202        return open;
203    }
204
205    /**
206     * Gets the Spring ApplicationContext.
207     */
208    protected synchronized ApplicationContext getContext() {
209        if (beanFactoryReference == null) {
210            if (log.isDebugEnabled() && beanFactoryReferenceLocation != null && beanRefFactoryReferenceId != null) {
211                log.debug(String.format("Starting Spring application context using bean factory [%s] from file: %s", beanRefFactoryReferenceId,
212                        beanFactoryReferenceLocation));
213            }
214            BeanFactoryLocator beanFactoryLocator =
215                    ContextSingletonBeanFactoryLocator.getInstance(
216                            beanFactoryReferenceLocation);
217            beanFactoryReference = beanFactoryLocator
218                    .useBeanFactory(beanRefFactoryReferenceId);
219            
220            open = true;
221        }
222        return (ApplicationContext) beanFactoryReference.getFactory();
223    }
224
225    @Override
226    public void close() throws IOException {        
227        shutdown();
228    }
229}