001/*
002 * (C) Copyright 2006-2007 Nuxeo SAS (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 *     Dragos Mihalache
016 */
017package org.nuxeo.ecm.core.uidgen;
018
019import java.util.HashMap;
020import java.util.LinkedHashMap;
021import java.util.Map;
022
023import org.apache.commons.logging.Log;
024import org.apache.commons.logging.LogFactory;
025import org.nuxeo.ecm.core.api.DocumentModel;
026import org.nuxeo.ecm.core.api.model.PropertyNotFoundException;
027import org.nuxeo.ecm.core.uidgen.UIDGenerator;
028import org.nuxeo.ecm.core.uidgen.UIDSequencer;
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<String, UIDGenerator>();
053
054    protected final Map<String, UIDSequencer> sequencers = new HashMap<String, UIDSequencer>();
055
056    protected final LinkedHashMap<String, UIDSequencerProviderDescriptor> sequencerContribs = new LinkedHashMap<String, UIDSequencerProviderDescriptor>();
057
058    protected String defaultSequencer;
059
060    @Override
061    public void activate(ComponentContext context) {
062        for (String name : sequencers.keySet()) {
063            sequencers.get(name).init();
064        }
065        super.activate(context);
066    }
067
068    @Override
069    public void deactivate(ComponentContext context) {
070        for (String name : sequencers.keySet()) {
071            sequencers.get(name).dispose();
072        }
073        super.deactivate(context);
074    }
075
076    @Override
077    public void registerExtension(Extension extension) {
078        super.registerExtension(extension);
079        final String extPoint = extension.getExtensionPoint();
080        if (UID_GENERATORS_EXTENSION_POINT.equals(extPoint)) {
081            log.info("register contributions for extension point: " + UID_GENERATORS_EXTENSION_POINT);
082            final Object[] contribs = extension.getContributions();
083            registerGenerators(extension, contribs);
084        } else if (SEQUENCERS_EXTENSION_POINT.equals(extPoint)) {
085            log.info("register contributions for extension point: " + SEQUENCERS_EXTENSION_POINT);
086            final Object[] contribs = extension.getContributions();
087            registerSequencers(extension, contribs);
088            computeDefault();
089        } else if (EXTENSION_POINT_SEQUENCER_FACTORY.equals(extPoint)) {
090            String msg = "UIDSequencer factory no more supported from version 5.4. Faulty component: "
091                    + extension.getComponent();
092            Framework.getRuntime().getWarnings().add(msg);
093            log.error(msg);
094        } else {
095            log.warn("extension not handled: " + extPoint);
096        }
097    }
098
099    @Override
100    public void unregisterExtension(Extension extension) {
101        final String extPoint = extension.getExtensionPoint();
102        if (UID_GENERATORS_EXTENSION_POINT.equals(extPoint)) {
103            log.info("unregister contributions for extension point: " + UID_GENERATORS_EXTENSION_POINT);
104            // final Object[] contribs = extension.getContributions();
105            // unregisterGenerators(extension, contribs);
106        } else if (SEQUENCERS_EXTENSION_POINT.equals(extPoint)) {
107            log.info("unregister contributions for extension point: " + SEQUENCERS_EXTENSION_POINT);
108            final Object[] contribs = extension.getContributions();
109            unregisterSequencers(extension, contribs);
110            computeDefault();
111        } else {
112            log.warn("extension not handled: " + extPoint);
113        }
114        super.unregisterExtension(extension);
115    }
116
117    protected void computeDefault() {
118        String def = null;
119        String last = null;
120        for (UIDSequencerProviderDescriptor contrib : sequencerContribs.values()) {
121            if (contrib.isIsdefault()) {
122                def = contrib.getName();
123            }
124            last = contrib.getName();
125        }
126
127        if (def == null) {
128            def = last;
129        }
130        defaultSequencer = def;
131    }
132
133    protected void registerSequencers(Extension extension, final Object[] contribs) {
134        for (Object contrib : contribs) {
135            UIDSequencerProviderDescriptor seqDescriptor = (UIDSequencerProviderDescriptor) contrib;
136            String name = seqDescriptor.getName();
137
138            try {
139                UIDSequencer seq = seqDescriptor.getSequencer();
140                if (seq != null) {
141                    seq.setName(name);
142                }
143                sequencers.put(name, seq);
144                sequencerContribs.put(name, seqDescriptor);
145            } catch (Exception e) {
146                log.error("Unable to create UIDSequencer with name " + name, e);
147            }
148        }
149    }
150
151    protected void unregisterSequencers(Extension extension, final Object[] contribs) {
152        for (Object contrib : contribs) {
153            UIDSequencerProviderDescriptor seqDescriptor = (UIDSequencerProviderDescriptor) contrib;
154            String name = seqDescriptor.getName();
155            sequencers.remove(name);
156            sequencerContribs.remove(name);
157        }
158    }
159
160    protected void registerGenerators(Extension extension, final Object[] contribs) {
161
162        // read the list of generators
163        for (Object contrib : contribs) {
164            final UIDGeneratorDescriptor generatorDescriptor = (UIDGeneratorDescriptor) contrib;
165            final String generatorName = generatorDescriptor.getName();
166
167            UIDGenerator generator;
168            try {
169                generator = (UIDGenerator) extension.getContext().loadClass(generatorDescriptor.getClassName()).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}