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