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