001/*
002 * (C) Copyright 2014 Nuxeo SA (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl-2.1.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     Florent Guillaume
016 */
017package org.nuxeo.ecm.core.storage.sql.coremodel;
018
019import java.lang.reflect.Field;
020import java.util.ArrayList;
021import java.util.HashMap;
022import java.util.List;
023import java.util.Map;
024
025import org.nuxeo.ecm.core.api.repository.Repository;
026import org.nuxeo.ecm.core.api.repository.RepositoryManager;
027import org.nuxeo.ecm.core.repository.RepositoryFactory;
028import org.nuxeo.ecm.core.repository.RepositoryService;
029import org.nuxeo.ecm.core.storage.FulltextConfiguration;
030import org.nuxeo.ecm.core.storage.FulltextParser;
031import org.nuxeo.ecm.core.storage.sql.RepositoryDescriptor;
032import org.nuxeo.ecm.core.storage.sql.RepositoryImpl;
033import org.nuxeo.ecm.core.storage.sql.RepositoryManagement;
034import org.nuxeo.runtime.api.Framework;
035import org.nuxeo.runtime.model.ComponentContext;
036import org.nuxeo.runtime.model.ComponentInstance;
037import org.nuxeo.runtime.model.DefaultComponent;
038import org.nuxeo.runtime.model.SimpleContributionRegistry;
039
040/**
041 * Service holding the configuration for VCS repositories.
042 *
043 * @since 5.9.3
044 */
045public class SQLRepositoryService extends DefaultComponent {
046
047    private static final String XP_REPOSITORY = "repository";
048
049    protected static final String CONNECTIONFACTORYIMPL_CLASS = "org.nuxeo.ecm.core.storage.sql.ra.ConnectionFactoryImpl";
050
051    protected RepositoryDescriptorRegistry registry = new RepositoryDescriptorRegistry();
052
053    protected static class RepositoryDescriptorRegistry extends SimpleContributionRegistry<RepositoryDescriptor> {
054
055        @Override
056        public String getContributionId(RepositoryDescriptor contrib) {
057            return contrib.name;
058        }
059
060        @Override
061        public RepositoryDescriptor clone(RepositoryDescriptor orig) {
062            return new RepositoryDescriptor(orig);
063        }
064
065        @Override
066        public void merge(RepositoryDescriptor src, RepositoryDescriptor dst) {
067            dst.merge(src);
068        }
069
070        @Override
071        public boolean isSupportingMerge() {
072            return true;
073        }
074
075        public void clear() {
076            currentContribs.clear();
077        }
078
079        public RepositoryDescriptor getRepositoryDescriptor(String id) {
080            return getCurrentContribution(id);
081        }
082
083        public List<String> getRepositoryIds() {
084            return new ArrayList<>(currentContribs.keySet());
085        }
086    }
087
088    @Override
089    public void activate(ComponentContext context) {
090        registry.clear();
091    }
092
093    @Override
094    public void deactivate(ComponentContext context) {
095        registry.clear();
096    }
097
098    @Override
099    public void registerContribution(Object contrib, String xpoint, ComponentInstance contributor) {
100        if (XP_REPOSITORY.equals(xpoint)) {
101            addContribution((RepositoryDescriptor) contrib);
102        } else {
103            throw new RuntimeException("Unknown extension point: " + xpoint);
104        }
105    }
106
107    @Override
108    public void unregisterContribution(Object contrib, String xpoint, ComponentInstance contributor) {
109        if (XP_REPOSITORY.equals(xpoint)) {
110            removeContribution((RepositoryDescriptor) contrib);
111        } else {
112            throw new RuntimeException("Unknown extension point: " + xpoint);
113        }
114    }
115
116    protected void addContribution(RepositoryDescriptor descriptor) {
117        registry.addContribution(descriptor);
118        updateRegistration(descriptor.name);
119    }
120
121    protected void removeContribution(RepositoryDescriptor descriptor) {
122        registry.removeContribution(descriptor);
123        updateRegistration(descriptor.name);
124    }
125
126    /**
127     * Update repository registration in high-level repository service.
128     */
129    protected void updateRegistration(String repositoryName) {
130        RepositoryManager repositoryManager = Framework.getLocalService(RepositoryManager.class);
131        RepositoryDescriptor descriptor = registry.getRepositoryDescriptor(repositoryName);
132        if (descriptor == null) {
133            // last contribution removed
134            repositoryManager.removeRepository(repositoryName);
135            return;
136        }
137        // extract label, isDefault and factory
138        // and pass it to high-level registry
139        Class<? extends RepositoryFactory> repositoryFactoryClass = descriptor.getRepositoryFactoryClass();
140        if (repositoryFactoryClass == null) {
141            // not the main contribution, just an override with
142            // much less info
143            repositoryManager.removeRepository(repositoryName);
144            return;
145        }
146        RepositoryFactory repositoryFactory;
147        try {
148            repositoryFactory = repositoryFactoryClass.newInstance();
149        } catch (InstantiationException | IllegalAccessException e) {
150            throw new RuntimeException("Cannot instantiate repository: " + repositoryName, e);
151        }
152        repositoryFactory.init(repositoryName);
153        Repository repository = new Repository(repositoryName, descriptor.label, descriptor.isDefault(),
154                repositoryFactory);
155        repositoryManager.addRepository(repository);
156    }
157
158    public RepositoryDescriptor getRepositoryDescriptor(String name) {
159        return registry.getRepositoryDescriptor(name);
160    }
161
162    /**
163     * Gets the list of SQL repository names.
164     *
165     * @return the list of SQL repository names
166     * @since 5.9.5
167     */
168    public List<String> getRepositoryNames() {
169        return registry.getRepositoryIds();
170    }
171
172    protected final Map<String, RepositoryImpl> testRepositories = new HashMap<String, RepositoryImpl>();
173
174    public void registerTestRepository(RepositoryImpl repository) {
175        testRepositories.put(repository.getName(), repository);
176    }
177
178    /**
179     * Gets the low-level SQL Repository of the given name.
180     *
181     * @param repositoryName the repository name
182     * @return the repository
183     * @since 5.9.5
184     */
185    public RepositoryManagement getRepository(String repositoryName) {
186        RepositoryService repositoryService = Framework.getLocalService(RepositoryService.class);
187        org.nuxeo.ecm.core.model.Repository repository = repositoryService.getRepository(repositoryName);
188        if (repository == null) {
189            RepositoryImpl repo = testRepositories.get(repositoryName);
190            if (repo != null) {
191                return repo;
192            }
193        }
194        if (repository == null) {
195            throw new RuntimeException("Unknown repository: " + repositoryName);
196        }
197        if (repository instanceof org.nuxeo.ecm.core.storage.sql.Repository) {
198            // (JCA) ConnectionFactoryImpl already implements Repository
199            return (org.nuxeo.ecm.core.storage.sql.Repository) repository;
200        } else if (repository instanceof SQLRepository) {
201            // (LocalSession not pooled) SQLRepository
202            // from SQLRepositoryFactory called by descriptor at registration
203            return ((SQLRepository) repository).repository;
204        } else {
205            throw new RuntimeException("Unknown repository class: " + repository.getClass().getName());
206        }
207    }
208
209    public RepositoryImpl getRepositoryImpl(String repositoryName) {
210        RepositoryManagement repository = getRepository(repositoryName);
211        if (repository instanceof RepositoryImpl) {
212            return (RepositoryImpl) repository;
213        }
214        if (!CONNECTIONFACTORYIMPL_CLASS.equals(repository.getClass().getName())) {
215            throw new RuntimeException("Unknown repository class: " + repository.getClass());
216        }
217        try {
218            Field f1 = repository.getClass().getDeclaredField("managedConnectionFactory");
219            f1.setAccessible(true);
220            Object factory = f1.get(repository);
221            Field f2 = factory.getClass().getDeclaredField("repository");
222            f2.setAccessible(true);
223            return (RepositoryImpl) f2.get(factory);
224        } catch (SecurityException | NoSuchFieldException | IllegalAccessException e) {
225            throw new RuntimeException(e);
226        }
227    }
228
229    /**
230     * Gets the repositories as a list of {@link RepositoryManagement} objects.
231     *
232     * @since 5.9.5
233     * @return a list of {@link RepositoryManagement}
234     */
235    public List<RepositoryManagement> getRepositories() {
236        List<RepositoryManagement> repositories = new ArrayList<RepositoryManagement>();
237        for (String repositoryName : getRepositoryNames()) {
238            repositories.add(getRepository(repositoryName));
239        }
240        return repositories;
241    }
242
243    public Class<? extends FulltextParser> getFulltextParserClass(String repositoryName) {
244        return getRepositoryImpl(repositoryName).getFulltextParserClass();
245    }
246
247    public FulltextConfiguration getFulltextConfiguration(String repositoryName) {
248        return getRepositoryImpl(repositoryName).getModel().getFulltextConfiguration();
249    }
250
251    /**
252     * Returns the datasource definition for the given repository and fills the properties map with the datasource
253     * configuration.
254     *
255     * @param repositoryName the repository name
256     * @param properties a return map of properties
257     * @return the XA datasource name, or null if single datasource is configured
258     * @since 5.9.5
259     */
260    public String getRepositoryDataSourceAndProperties(String repositoryName, Map<String, String> properties) {
261        RepositoryDescriptor desc = getRepositoryImpl(repositoryName).getRepositoryDescriptor();
262        if (desc.properties != null) {
263            properties.putAll(desc.properties);
264        }
265        return desc.xaDataSourceName;
266    }
267
268}