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                UIDSequencer seq = seqDescriptor.getSequencer();
138                if (seq != null) {
139                    seq.setName(name);
140                }
141                sequencers.put(name, seq);
142                sequencerContribs.put(name, seqDescriptor);
143            } catch (Exception e) {
144                log.error("Unable to create UIDSequencer with name " + name, e);
145            }
146        }
147    }
148
149    protected void unregisterSequencers(Extension extension, final Object[] contribs) {
150        for (Object contrib : contribs) {
151            UIDSequencerProviderDescriptor seqDescriptor = (UIDSequencerProviderDescriptor) contrib;
152            String name = seqDescriptor.getName();
153            sequencers.remove(name);
154            sequencerContribs.remove(name);
155        }
156    }
157
158    protected void registerGenerators(Extension extension, final Object[] contribs) {
159
160        // read the list of generators
161        for (Object contrib : contribs) {
162            final UIDGeneratorDescriptor generatorDescriptor = (UIDGeneratorDescriptor) contrib;
163            final String generatorName = generatorDescriptor.getName();
164
165            UIDGenerator generator;
166            try {
167                generator = (UIDGenerator) extension.getContext()
168                                                    .loadClass(generatorDescriptor.getClassName())
169                                                    .newInstance();
170            } catch (ReflectiveOperationException e) {
171                throw new RuntimeException(e);
172            }
173
174            final String[] propNames = generatorDescriptor.getPropertyNames();
175            if (propNames.length == 0) {
176                log.error("no property name defined on generator " + generatorName);
177            }
178            // set the property name on generator
179            generator.setPropertyNames(propNames);
180
181            // Register Generator for DocTypes and property name
182            final String[] docTypes = generatorDescriptor.getDocTypes();
183            registerGeneratorForDocTypes(generator, docTypes);
184
185            log.info("registered UID generator: " + generatorName);
186        }
187    }
188
189    /**
190     * Registers given UIDGenerator for the given document types. If there is already a generator registered for one of
191     * document type it will be discarded (and replaced with the new generator).
192     */
193    private void registerGeneratorForDocTypes(final UIDGenerator generator, final String[] docTypes) {
194
195        for (String docType : docTypes) {
196            final UIDGenerator previous = generators.put(docType, generator);
197            if (previous != null) {
198                log.info("Overwriting generator: " + previous.getClass() + " for docType: " + docType);
199            }
200            log.info("Registered generator: " + generator.getClass() + " for docType: " + docType);
201        }
202    }
203
204    /**
205     * Returns the uid generator to use for this document.
206     * <p>
207     * Choice is made following the document type and the generator configuration.
208     */
209    @Override
210    public UIDGenerator getUIDGeneratorFor(DocumentModel doc) {
211        final String docTypeName = doc.getType();
212        final UIDGenerator generator = generators.get(docTypeName);
213
214        if (generator == null) {
215            log.debug("No UID Generator defined for doc type: " + docTypeName);
216            return null;
217        }
218        // TODO maybe maintain an initialization state for generators
219        // so the next call could be avoided (for each request)
220        generator.setSequencer(Framework.getService(UIDSequencer.class));
221
222        return generator;
223    }
224
225    /**
226     * Creates a new UID for the given doc and sets the field configured in the generator component with this value.
227     */
228    @Override
229    public void setUID(DocumentModel doc) throws PropertyNotFoundException {
230        final UIDGenerator generator = getUIDGeneratorFor(doc);
231        if (generator != null) {
232            generator.setUID(doc);
233        }
234    }
235
236    /**
237     * @return a new UID for the given document
238     */
239    @Override
240    public String createUID(DocumentModel doc) {
241        final UIDGenerator generator = getUIDGeneratorFor(doc);
242        if (generator == null) {
243            return null;
244        } else {
245            return generator.createUID(doc);
246        }
247    }
248
249    @Override
250    public <T> T getAdapter(Class<T> adapter) {
251        if (UIDSequencer.class.isAssignableFrom(adapter)) {
252            return adapter.cast(getSequencer());
253        }
254        if (UIDGeneratorService.class.isAssignableFrom(adapter)) {
255            return adapter.cast(this);
256        }
257        return null;
258    }
259
260    @Override
261    public UIDSequencer getSequencer() {
262        return getSequencer(null);
263    }
264
265    @Override
266    public UIDSequencer getSequencer(String name) {
267        if (name == null) {
268            name = defaultSequencer;
269        }
270        return sequencers.get(name);
271    }
272
273}