001/*
002 * (C) Copyright 2006-2017 Nuxeo (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 *     Dragos Mihalache
018 */
019package org.nuxeo.ecm.core.uidgen;
020
021import java.util.HashMap;
022import java.util.LinkedHashMap;
023import java.util.Map;
024
025import org.apache.commons.logging.Log;
026import org.apache.commons.logging.LogFactory;
027import org.nuxeo.ecm.core.api.DocumentModel;
028import org.nuxeo.ecm.core.api.model.PropertyNotFoundException;
029import org.nuxeo.runtime.api.Framework;
030import org.nuxeo.runtime.model.ComponentContext;
031import org.nuxeo.runtime.model.DefaultComponent;
032import org.nuxeo.runtime.model.Extension;
033
034/**
035 * Service that writes MetaData.
036 */
037public class UIDGeneratorComponent extends DefaultComponent implements UIDGeneratorService {
038
039    public static final String ID = "org.nuxeo.ecm.core.uidgen.UIDGeneratorService";
040
041    public static final String UID_GENERATORS_EXTENSION_POINT = "generators";
042
043    public static final String SEQUENCERS_EXTENSION_POINT = "sequencers";
044
045    /**
046     * Extension point is deprecated should be removed - preserved for now only for startup warnings.
047     */
048    public static final String EXTENSION_POINT_SEQUENCER_FACTORY = "sequencerFactory";
049
050    private static final Log log = LogFactory.getLog(UIDGeneratorComponent.class);
051
052    protected final Map<String, UIDGenerator> generators = new HashMap<>();
053
054    protected final Map<String, UIDSequencer> sequencers = new HashMap<>();
055
056    protected final LinkedHashMap<String, UIDSequencerProviderDescriptor> sequencerContribs = new LinkedHashMap<>();
057
058    protected String defaultSequencer;
059
060    @Override
061    public void start(ComponentContext context) {
062        for (String name : sequencers.keySet()) {
063            sequencers.get(name).init();
064        }
065    }
066
067    @Override
068    public void stop(ComponentContext context) {
069        for (String name : sequencers.keySet()) {
070            sequencers.get(name).dispose();
071        }
072    }
073
074    @Override
075    public void registerExtension(Extension extension) {
076        super.registerExtension(extension);
077        final String extPoint = extension.getExtensionPoint();
078        if (UID_GENERATORS_EXTENSION_POINT.equals(extPoint)) {
079            log.info("register contributions for extension point: " + UID_GENERATORS_EXTENSION_POINT);
080            final Object[] contribs = extension.getContributions();
081            registerGenerators(extension, contribs);
082        } else if (SEQUENCERS_EXTENSION_POINT.equals(extPoint)) {
083            log.info("register contributions for extension point: " + SEQUENCERS_EXTENSION_POINT);
084            final Object[] contribs = extension.getContributions();
085            registerSequencers(extension, contribs);
086            computeDefault();
087        } else if (EXTENSION_POINT_SEQUENCER_FACTORY.equals(extPoint)) {
088            String msg = "UIDSequencer factory no more supported from version 5.4. Faulty component: "
089                    + extension.getComponent();
090            Framework.getRuntime().getMessageHandler().addWarning(msg);
091            log.error(msg);
092        } else {
093            log.warn("extension not handled: " + extPoint);
094        }
095    }
096
097    @Override
098    public void unregisterExtension(Extension extension) {
099        final String extPoint = extension.getExtensionPoint();
100        if (UID_GENERATORS_EXTENSION_POINT.equals(extPoint)) {
101            log.info("unregister contributions for extension point: " + UID_GENERATORS_EXTENSION_POINT);
102            // final Object[] contribs = extension.getContributions();
103            // unregisterGenerators(extension, contribs);
104        } else if (SEQUENCERS_EXTENSION_POINT.equals(extPoint)) {
105            log.info("unregister contributions for extension point: " + SEQUENCERS_EXTENSION_POINT);
106            final Object[] contribs = extension.getContributions();
107            unregisterSequencers(extension, contribs);
108            computeDefault();
109        } else {
110            log.warn("extension not handled: " + extPoint);
111        }
112        super.unregisterExtension(extension);
113    }
114
115    protected void computeDefault() {
116        String def = null;
117        String last = null;
118        for (UIDSequencerProviderDescriptor contrib : sequencerContribs.values()) {
119            if (contrib.isIsdefault()) {
120                def = contrib.getName();
121            }
122            last = contrib.getName();
123        }
124
125        if (def == null) {
126            def = last;
127        }
128        defaultSequencer = def;
129    }
130
131    protected void registerSequencers(Extension extension, final Object[] contribs) {
132        for (Object contrib : contribs) {
133            UIDSequencerProviderDescriptor seqDescriptor = (UIDSequencerProviderDescriptor) contrib;
134            String name = seqDescriptor.getName();
135
136            try {
137                if (seqDescriptor.isEnabled()) {
138                    UIDSequencer seq = seqDescriptor.getSequencer();
139                    if (seq != null) {
140                        seq.setName(name);
141                    }
142                    sequencers.put(name, seq);
143                    sequencerContribs.put(name, seqDescriptor);
144
145                } else {
146                    log.info(String.format("Sequencer %s is disabled.", name));
147                }
148            } catch (Exception e) {
149                log.error("Unable to create UIDSequencer with name " + name, e);
150            }
151        }
152    }
153
154    protected void unregisterSequencers(Extension extension, final Object[] contribs) {
155        for (Object contrib : contribs) {
156            UIDSequencerProviderDescriptor seqDescriptor = (UIDSequencerProviderDescriptor) contrib;
157            String name = seqDescriptor.getName();
158            sequencers.remove(name);
159            sequencerContribs.remove(name);
160        }
161    }
162
163    protected void registerGenerators(Extension extension, final Object[] contribs) {
164
165        // read the list of generators
166        for (Object contrib : contribs) {
167            final UIDGeneratorDescriptor generatorDescriptor = (UIDGeneratorDescriptor) contrib;
168            final String generatorName = generatorDescriptor.getName();
169
170            UIDGenerator generator;
171            try {
172                generator = (UIDGenerator) extension.getContext()
173                                                    .loadClass(generatorDescriptor.getClassName())
174                                                    .newInstance();
175            } catch (ReflectiveOperationException e) {
176                throw new RuntimeException(e);
177            }
178
179            final String[] propNames = generatorDescriptor.getPropertyNames();
180            if (propNames.length == 0) {
181                log.error("no property name defined on generator " + generatorName);
182            }
183            // set the property name on generator
184            generator.setPropertyNames(propNames);
185
186            // Register Generator for DocTypes and property name
187            final String[] docTypes = generatorDescriptor.getDocTypes();
188            registerGeneratorForDocTypes(generator, docTypes);
189
190            log.info("registered UID generator: " + generatorName);
191        }
192    }
193
194    /**
195     * Registers given UIDGenerator for the given document types. If there is already a generator registered for one of
196     * document type it will be discarded (and replaced with the new generator).
197     */
198    private void registerGeneratorForDocTypes(final UIDGenerator generator, final String[] docTypes) {
199
200        for (String docType : docTypes) {
201            final UIDGenerator previous = generators.put(docType, generator);
202            if (previous != null) {
203                log.info("Overwriting generator: " + previous.getClass() + " for docType: " + docType);
204            }
205            log.info("Registered generator: " + generator.getClass() + " for docType: " + docType);
206        }
207    }
208
209    /**
210     * Returns the uid generator to use for this document.
211     * <p>
212     * Choice is made following the document type and the generator configuration.
213     */
214    @Override
215    public UIDGenerator getUIDGeneratorFor(DocumentModel doc) {
216        final String docTypeName = doc.getType();
217        final UIDGenerator generator = generators.get(docTypeName);
218
219        if (generator == null) {
220            log.debug("No UID Generator defined for doc type: " + docTypeName);
221            return null;
222        }
223        // TODO maybe maintain an initialization state for generators
224        // so the next call could be avoided (for each request)
225        generator.setSequencer(Framework.getService(UIDSequencer.class));
226
227        return generator;
228    }
229
230    /**
231     * Creates a new UID for the given doc and sets the field configured in the generator component with this value.
232     */
233    @Override
234    public void setUID(DocumentModel doc) throws PropertyNotFoundException {
235        final UIDGenerator generator = getUIDGeneratorFor(doc);
236        if (generator != null) {
237            generator.setUID(doc);
238        }
239    }
240
241    /**
242     * @return a new UID for the given document
243     */
244    @Override
245    public String createUID(DocumentModel doc) {
246        final UIDGenerator generator = getUIDGeneratorFor(doc);
247        if (generator == null) {
248            return null;
249        } else {
250            return generator.createUID(doc);
251        }
252    }
253
254    @Override
255    public <T> T getAdapter(Class<T> adapter) {
256        if (UIDSequencer.class.isAssignableFrom(adapter)) {
257            return adapter.cast(getSequencer());
258        }
259        if (UIDGeneratorService.class.isAssignableFrom(adapter)) {
260            return adapter.cast(this);
261        }
262        return null;
263    }
264
265    @Override
266    public UIDSequencer getSequencer() {
267        return getSequencer(null);
268    }
269
270    @Override
271    public UIDSequencer getSequencer(String name) {
272        if (name == null) {
273            name = defaultSequencer;
274        }
275        return sequencers.get(name);
276    }
277
278}