001/*
002 * (C) Copyright 2014 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Florent Guillaume
018 */
019package org.nuxeo.ecm.core.storage.sql.coremodel;
020
021import java.lang.reflect.Field;
022import java.util.ArrayList;
023import java.util.HashMap;
024import java.util.List;
025import java.util.Map;
026
027import org.nuxeo.ecm.core.api.repository.Repository;
028import org.nuxeo.ecm.core.api.repository.RepositoryManager;
029import org.nuxeo.ecm.core.repository.RepositoryFactory;
030import org.nuxeo.ecm.core.repository.RepositoryService;
031import org.nuxeo.ecm.core.storage.FulltextConfiguration;
032import org.nuxeo.ecm.core.storage.FulltextParser;
033import org.nuxeo.ecm.core.storage.sql.RepositoryDescriptor;
034import org.nuxeo.ecm.core.storage.sql.RepositoryImpl;
035import org.nuxeo.ecm.core.storage.sql.RepositoryManagement;
036import org.nuxeo.ecm.core.storage.sql.ra.PoolingRepositoryFactory;
037import org.nuxeo.runtime.api.Framework;
038import org.nuxeo.runtime.model.ComponentContext;
039import org.nuxeo.runtime.model.ComponentInstance;
040import org.nuxeo.runtime.model.DefaultComponent;
041import org.nuxeo.runtime.model.SimpleContributionRegistry;
042
043/**
044 * Service holding the configuration for VCS repositories.
045 *
046 * @since 5.9.3
047 */
048public class SQLRepositoryService extends DefaultComponent {
049
050    private static final String XP_REPOSITORY = "repository";
051
052    protected static final String CONNECTIONFACTORYIMPL_CLASS = "org.nuxeo.ecm.core.storage.sql.ra.ConnectionFactoryImpl";
053
054    protected RepositoryDescriptorRegistry registry = new RepositoryDescriptorRegistry();
055
056    protected static class RepositoryDescriptorRegistry extends SimpleContributionRegistry<RepositoryDescriptor> {
057
058        @Override
059        public String getContributionId(RepositoryDescriptor contrib) {
060            return contrib.name;
061        }
062
063        @Override
064        public RepositoryDescriptor clone(RepositoryDescriptor orig) {
065            return new RepositoryDescriptor(orig);
066        }
067
068        @Override
069        public void merge(RepositoryDescriptor src, RepositoryDescriptor dst) {
070            dst.merge(src);
071        }
072
073        @Override
074        public boolean isSupportingMerge() {
075            return true;
076        }
077
078        public void clear() {
079            currentContribs.clear();
080        }
081
082        public RepositoryDescriptor getRepositoryDescriptor(String id) {
083            return getCurrentContribution(id);
084        }
085
086        public List<String> getRepositoryIds() {
087            return new ArrayList<>(currentContribs.keySet());
088        }
089    }
090
091    @Override
092    public void activate(ComponentContext context) {
093        registry.clear();
094    }
095
096    @Override
097    public void deactivate(ComponentContext context) {
098        registry.clear();
099    }
100
101    @Override
102    public void registerContribution(Object contrib, String xpoint, ComponentInstance contributor) {
103        if (XP_REPOSITORY.equals(xpoint)) {
104            addContribution((RepositoryDescriptor) contrib);
105        } else {
106            throw new RuntimeException("Unknown extension point: " + xpoint);
107        }
108    }
109
110    @Override
111    public void unregisterContribution(Object contrib, String xpoint, ComponentInstance contributor) {
112        if (XP_REPOSITORY.equals(xpoint)) {
113            removeContribution((RepositoryDescriptor) contrib);
114        } else {
115            throw new RuntimeException("Unknown extension point: " + xpoint);
116        }
117    }
118
119    protected void addContribution(RepositoryDescriptor descriptor) {
120        registry.addContribution(descriptor);
121        updateRegistration(descriptor.name);
122    }
123
124    protected void removeContribution(RepositoryDescriptor descriptor) {
125        registry.removeContribution(descriptor);
126        updateRegistration(descriptor.name);
127    }
128
129    /**
130     * Update repository registration in high-level repository service.
131     */
132    protected void updateRegistration(String repositoryName) {
133        RepositoryManager repositoryManager = Framework.getService(RepositoryManager.class);
134        RepositoryDescriptor descriptor = registry.getRepositoryDescriptor(repositoryName);
135        if (descriptor == null) {
136            // last contribution removed
137            repositoryManager.removeRepository(repositoryName);
138            return;
139        }
140        // extract label, isDefault
141        // and pass it to high-level registry
142        RepositoryFactory repositoryFactory = new PoolingRepositoryFactory(repositoryName);
143        Repository repository = new Repository(repositoryName, descriptor.label, descriptor.isDefault(),
144                repositoryFactory);
145        repositoryManager.addRepository(repository);
146    }
147
148    public RepositoryDescriptor getRepositoryDescriptor(String name) {
149        return registry.getRepositoryDescriptor(name);
150    }
151
152    /**
153     * Gets the list of SQL repository names.
154     *
155     * @return the list of SQL repository names
156     * @since 5.9.5
157     */
158    public List<String> getRepositoryNames() {
159        return registry.getRepositoryIds();
160    }
161
162    protected final Map<String, RepositoryImpl> testRepositories = new HashMap<String, RepositoryImpl>();
163
164    public void registerTestRepository(RepositoryImpl repository) {
165        testRepositories.put(repository.getName(), repository);
166    }
167
168    /**
169     * Gets the low-level SQL Repository of the given name.
170     *
171     * @param repositoryName the repository name
172     * @return the repository
173     * @since 5.9.5
174     */
175    public RepositoryManagement getRepository(String repositoryName) {
176        RepositoryService repositoryService = Framework.getService(RepositoryService.class);
177        org.nuxeo.ecm.core.model.Repository repository = repositoryService.getRepository(repositoryName);
178        if (repository == null) {
179            RepositoryImpl repo = testRepositories.get(repositoryName);
180            if (repo != null) {
181                return repo;
182            }
183        }
184        if (repository == null) {
185            throw new RuntimeException("Unknown repository: " + repositoryName);
186        }
187        if (repository instanceof org.nuxeo.ecm.core.storage.sql.Repository) {
188            // (JCA) ConnectionFactoryImpl already implements Repository
189            return (org.nuxeo.ecm.core.storage.sql.Repository) repository;
190        } else {
191            throw new RuntimeException("Unknown repository class: " + repository.getClass().getName());
192        }
193    }
194
195    public RepositoryImpl getRepositoryImpl(String repositoryName) {
196        RepositoryManagement repository = getRepository(repositoryName);
197        if (repository instanceof RepositoryImpl) {
198            return (RepositoryImpl) repository;
199        }
200        if (!CONNECTIONFACTORYIMPL_CLASS.equals(repository.getClass().getName())) {
201            throw new RuntimeException("Unknown repository class: " + repository.getClass());
202        }
203        try {
204            Field f1 = repository.getClass().getDeclaredField("managedConnectionFactory");
205            f1.setAccessible(true);
206            Object factory = f1.get(repository);
207            Field f2 = factory.getClass().getDeclaredField("repository");
208            f2.setAccessible(true);
209            return (RepositoryImpl) f2.get(factory);
210        } catch (SecurityException | NoSuchFieldException | IllegalAccessException e) {
211            throw new RuntimeException(e);
212        }
213    }
214
215    /**
216     * Gets the repositories as a list of {@link RepositoryManagement} objects.
217     *
218     * @since 5.9.5
219     * @return a list of {@link RepositoryManagement}
220     */
221    public List<RepositoryManagement> getRepositories() {
222        List<RepositoryManagement> repositories = new ArrayList<RepositoryManagement>();
223        for (String repositoryName : getRepositoryNames()) {
224            repositories.add(getRepository(repositoryName));
225        }
226        return repositories;
227    }
228
229    public Class<? extends FulltextParser> getFulltextParserClass(String repositoryName) {
230        return getRepositoryImpl(repositoryName).getFulltextParserClass();
231    }
232
233    public FulltextConfiguration getFulltextConfiguration(String repositoryName) {
234        return getRepositoryImpl(repositoryName).getModel().getFulltextConfiguration();
235    }
236
237}