001/*
002 * (C) Copyright 2006-2009 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 *     Nuxeo
018 */
019
020package org.nuxeo.ecm.platform.publisher.impl.service;
021
022import java.util.ArrayList;
023import java.util.HashMap;
024import java.util.Iterator;
025import java.util.List;
026import java.util.Map;
027
028import org.apache.commons.logging.Log;
029import org.apache.commons.logging.LogFactory;
030import org.nuxeo.common.utils.Path;
031import org.nuxeo.ecm.core.api.CoreInstance;
032import org.nuxeo.ecm.core.api.CoreSession;
033import org.nuxeo.ecm.core.api.DocumentLocation;
034import org.nuxeo.ecm.core.api.DocumentModel;
035import org.nuxeo.ecm.core.api.NuxeoException;
036import org.nuxeo.ecm.core.api.repository.RepositoryManager;
037import org.nuxeo.ecm.core.repository.RepositoryService;
038import org.nuxeo.ecm.platform.publisher.api.PublicationNode;
039import org.nuxeo.ecm.platform.publisher.api.PublicationTree;
040import org.nuxeo.ecm.platform.publisher.api.PublicationTreeNotAvailable;
041import org.nuxeo.ecm.platform.publisher.api.PublishedDocument;
042import org.nuxeo.ecm.platform.publisher.api.PublishedDocumentFactory;
043import org.nuxeo.ecm.platform.publisher.api.PublisherService;
044import org.nuxeo.ecm.platform.publisher.api.RemotePublicationTreeManager;
045import org.nuxeo.ecm.platform.publisher.descriptors.PublicationTreeConfigDescriptor;
046import org.nuxeo.ecm.platform.publisher.descriptors.PublicationTreeDescriptor;
047import org.nuxeo.ecm.platform.publisher.descriptors.PublishedDocumentFactoryDescriptor;
048import org.nuxeo.ecm.platform.publisher.descriptors.RootSectionFinderFactoryDescriptor;
049import org.nuxeo.ecm.platform.publisher.helper.PublicationRelationHelper;
050import org.nuxeo.ecm.platform.publisher.helper.RootSectionFinder;
051import org.nuxeo.ecm.platform.publisher.helper.RootSectionFinderFactory;
052import org.nuxeo.ecm.platform.publisher.impl.finder.DefaultRootSectionsFinder;
053import org.nuxeo.ecm.platform.publisher.rules.ValidatorsRule;
054import org.nuxeo.ecm.platform.publisher.rules.ValidatorsRuleDescriptor;
055import org.nuxeo.runtime.api.Framework;
056import org.nuxeo.runtime.model.ComponentContext;
057import org.nuxeo.runtime.model.ComponentInstance;
058import org.nuxeo.runtime.model.DefaultComponent;
059import org.nuxeo.runtime.transaction.TransactionHelper;
060
061/**
062 * POJO implementation of the publisher service Implements both {@link PublisherService} and
063 * {@link RemotePublicationTreeManager}.
064 *
065 * @author tiry
066 */
067public class PublisherServiceImpl extends DefaultComponent implements PublisherService, RemotePublicationTreeManager {
068
069    private final Log log = LogFactory.getLog(PublisherServiceImpl.class);
070
071    protected Map<String, PublicationTreeDescriptor> treeDescriptors = new HashMap<String, PublicationTreeDescriptor>();
072
073    protected Map<String, PublishedDocumentFactoryDescriptor> factoryDescriptors = new HashMap<String, PublishedDocumentFactoryDescriptor>();
074
075    protected Map<String, PublicationTreeConfigDescriptor> treeConfigDescriptors = new HashMap<String, PublicationTreeConfigDescriptor>();
076
077    protected Map<String, ValidatorsRuleDescriptor> validatorsRuleDescriptors = new HashMap<String, ValidatorsRuleDescriptor>();
078
079    protected Map<String, PublicationTreeConfigDescriptor> pendingDescriptors = new HashMap<String, PublicationTreeConfigDescriptor>();
080
081    protected Map<String, PublicationTree> liveTrees = new HashMap<String, PublicationTree>();
082
083    protected RootSectionFinderFactory rootSectionFinderFactory = null;
084
085    // Store association between treeSid and CoreSession that was opened locally
086    // for them : this unable proper cleanup of allocated sessions
087    protected Map<String, String> remoteLiveTrees = new HashMap<String, String>();
088
089    public static final String TREE_EP = "tree";
090
091    public static final String TREE_CONFIG_EP = "treeInstance";
092
093    public static final String VALIDATORS_RULE_EP = "validatorsRule";
094
095    public static final String FACTORY_EP = "factory";
096
097    public static final String ROOT_SECTION_FINDER_FACTORY_EP = "rootSectionFinderFactory";
098
099    protected static final String ROOT_PATH_KEY = "RootPath";
100
101    protected static final String RELATIVE_ROOT_PATH_KEY = "RelativeRootPath";
102
103    @Override
104    public void applicationStarted(ComponentContext context) {
105        RepositoryService repositoryService = Framework.getService(RepositoryService.class);
106        if (repositoryService == null) {
107            // RepositoryService failed to start, no need to go further
108            return;
109        }
110        boolean txWasStartedOutsideComponent = TransactionHelper.isTransactionActiveOrMarkedRollback();
111
112        if (txWasStartedOutsideComponent || TransactionHelper.startTransaction()) {
113            boolean completedAbruptly = true;
114            try {
115                doApplicationStarted();
116                completedAbruptly = false;
117            } finally {
118                if (completedAbruptly) {
119                    TransactionHelper.setTransactionRollbackOnly();
120                }
121                if (!txWasStartedOutsideComponent) {
122                    TransactionHelper.commitOrRollbackTransaction();
123                }
124            }
125        } else {
126            doApplicationStarted();
127        }
128    }
129
130    protected void doApplicationStarted() {
131        ClassLoader jbossCL = Thread.currentThread().getContextClassLoader();
132        ClassLoader nuxeoCL = PublisherServiceImpl.class.getClassLoader();
133        try {
134            Thread.currentThread().setContextClassLoader(nuxeoCL);
135            log.info("Publisher Service initialization");
136            registerPendingDescriptors();
137        } finally {
138            Thread.currentThread().setContextClassLoader(jbossCL);
139            log.debug("JBoss ClassLoader restored");
140        }
141    }
142
143    @Override
144    public void activate(ComponentContext context) {
145        liveTrees = new HashMap<String, PublicationTree>();
146        treeDescriptors = new HashMap<String, PublicationTreeDescriptor>();
147        factoryDescriptors = new HashMap<String, PublishedDocumentFactoryDescriptor>();
148        treeConfigDescriptors = new HashMap<String, PublicationTreeConfigDescriptor>();
149        validatorsRuleDescriptors = new HashMap<String, ValidatorsRuleDescriptor>();
150        pendingDescriptors = new HashMap<String, PublicationTreeConfigDescriptor>();
151    }
152
153    // for testing cleanup
154    public int getLiveTreeCount() {
155        return liveTrees.size();
156    }
157
158    public PublicationTree getTreeBySid(String sid) {
159        return liveTrees.get(sid);
160    }
161
162    @Override
163    public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
164
165        log.debug("Registry contribution for EP " + extensionPoint);
166
167        if (TREE_EP.equals(extensionPoint)) {
168            PublicationTreeDescriptor desc = (PublicationTreeDescriptor) contribution;
169            treeDescriptors.put(desc.getName(), desc);
170        } else if (TREE_CONFIG_EP.equals(extensionPoint)) {
171            PublicationTreeConfigDescriptor desc = (PublicationTreeConfigDescriptor) contribution;
172            registerTreeConfig(desc);
173        } else if (FACTORY_EP.equals(extensionPoint)) {
174            PublishedDocumentFactoryDescriptor desc = (PublishedDocumentFactoryDescriptor) contribution;
175            factoryDescriptors.put(desc.getName(), desc);
176        } else if (VALIDATORS_RULE_EP.equals(extensionPoint)) {
177            ValidatorsRuleDescriptor desc = (ValidatorsRuleDescriptor) contribution;
178            validatorsRuleDescriptors.put(desc.getName(), desc);
179        } else if (ROOT_SECTION_FINDER_FACTORY_EP.equals(extensionPoint)) {
180            RootSectionFinderFactoryDescriptor desc = (RootSectionFinderFactoryDescriptor) contribution;
181            try {
182                rootSectionFinderFactory = desc.getFactory().newInstance();
183            } catch (ReflectiveOperationException t) {
184                log.error("Unable to load custom RootSectionFinderFactory", t);
185            }
186        }
187    }
188
189    protected void registerTreeConfig(PublicationTreeConfigDescriptor desc) {
190        if (desc.getParameters().get("RelativeRootPath") != null) {
191            pendingDescriptors.put(desc.getName(), desc);
192        } else {
193            treeConfigDescriptors.put(desc.getName(), desc);
194        }
195    }
196
197    @Override
198    public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
199        if (contribution instanceof PublicationTreeDescriptor) {
200            treeDescriptors.remove(((PublicationTreeDescriptor) contribution).getName());
201        } else if (contribution instanceof PublicationTreeConfigDescriptor) {
202            String name = ((PublicationTreeConfigDescriptor) contribution).getName();
203            pendingDescriptors.remove(name);
204            treeConfigDescriptors.remove(name);
205        } else if (contribution instanceof ValidatorsRuleDescriptor) {
206            validatorsRuleDescriptors.remove(((ValidatorsRuleDescriptor) contribution).getName());
207        } else if (contribution instanceof RootSectionFinderFactoryDescriptor) {
208            rootSectionFinderFactory = null;
209        }
210    }
211
212    protected String computeTreeSessionId(String treeConfigName, CoreSession coreSession) {
213        return computeTreeSessionId(treeConfigName, coreSession.getSessionId());
214    }
215
216    protected String computeTreeSessionId(String treeConfigName, String sid) {
217        return treeConfigName + sid;
218    }
219
220    @Override
221    public List<String> getAvailablePublicationTree() {
222        List<String> treeConfigs = new ArrayList<String>();
223        treeConfigs.addAll(treeConfigDescriptors.keySet());
224        return treeConfigs;
225    }
226
227    @Override
228    public Map<String, String> getAvailablePublicationTrees() {
229        Map<String, String> trees = new HashMap<String, String>();
230        for (PublicationTreeConfigDescriptor desc : treeConfigDescriptors.values()) {
231            String title = desc.getTitle() == null ? desc.getName() : desc.getTitle();
232            trees.put(desc.getName(), title);
233        }
234        return trees;
235    }
236
237    @Override
238    public PublicationTree getPublicationTree(String treeName, CoreSession coreSession, Map<String, String> params)
239            throws PublicationTreeNotAvailable {
240        return getPublicationTree(treeName, coreSession, params, null);
241    }
242
243    @Override
244    public PublicationTree getPublicationTree(String treeName, CoreSession coreSession, Map<String, String> params,
245            DocumentModel currentDocument) throws PublicationTreeNotAvailable {
246        PublicationTree tree = getOrBuildTree(treeName, coreSession, params);
247        if (tree == null) {
248            return null;
249        }
250        if (currentDocument != null) {
251            tree.setCurrentDocument(currentDocument);
252        }
253        return new ProxyTree(tree, tree.getSessionId());
254    }
255
256    @Override
257    public Map<String, String> initRemoteSession(String treeConfigName, Map<String, String> params) {
258        CoreSession coreSession = CoreInstance.openCoreSession(null);
259        PublicationTree tree = getPublicationTree(treeConfigName, coreSession, params);
260
261        remoteLiveTrees.put(tree.getSessionId(), coreSession.getSessionId());
262
263        Map<String, String> res = new HashMap<String, String>();
264        res.put("sessionId", tree.getSessionId());
265        res.put("title", tree.getTitle());
266        res.put("nodeType", tree.getNodeType());
267        res.put("treeName", tree.getConfigName());
268        res.put("path", tree.getPath());
269
270        return res;
271    }
272
273    @Override
274    public void release(String sid) {
275        PublicationTree tree;
276
277        if (liveTrees.containsKey(sid)) {
278            tree = liveTrees.get(sid);
279            tree.release();
280            liveTrees.remove(sid);
281        }
282        if (remoteLiveTrees.containsKey(sid)) {
283            // close here session opened for remote trees
284            String sessionId = remoteLiveTrees.get(sid);
285            CoreSession remoteSession = CoreInstance.getInstance().getSession(sessionId);
286            remoteSession.close();
287            remoteLiveTrees.remove(sid);
288        }
289    }
290
291    @Override
292    public void releaseAllTrees(String sessionId) {
293        for (String configName : treeConfigDescriptors.keySet()) {
294            String treeid = computeTreeSessionId(configName, sessionId);
295            release(treeid);
296        }
297    }
298
299    protected PublicationTree getOrBuildTree(String treeConfigName, CoreSession coreSession, Map<String, String> params)
300            throws PublicationTreeNotAvailable {
301        String key = computeTreeSessionId(treeConfigName, coreSession);
302        PublicationTree tree;
303        if (liveTrees.containsKey(key)) {
304            tree = liveTrees.get(key);
305        } else {
306            tree = buildTree(key, treeConfigName, coreSession, params);
307            if (tree != null) {
308                liveTrees.put(key, tree);
309            }
310        }
311        return tree;
312    }
313
314    protected PublicationTree buildTree(String sid, String treeConfigName, CoreSession coreSession,
315            Map<String, String> params) throws PublicationTreeNotAvailable {
316        PublicationTreeConfigDescriptor config = getPublicationTreeConfigDescriptor(treeConfigName);
317        Map<String, String> allParameters = computeAllParameters(config, params);
318        PublicationTreeDescriptor treeDescriptor = getPublicationTreeDescriptor(config);
319        PublishedDocumentFactory publishedDocumentFactory = getPublishedDocumentFactory(config, treeDescriptor,
320                coreSession, allParameters);
321        return getPublicationTree(treeDescriptor, sid, coreSession, allParameters, publishedDocumentFactory,
322                config.getName(), config.getTitle());
323    }
324
325    protected Map<String, String> computeAllParameters(PublicationTreeConfigDescriptor config,
326            Map<String, String> params) {
327        final Map<String, String> allParameters = config.getParameters();
328        if (params != null) {
329            allParameters.putAll(params);
330        }
331        return allParameters;
332    }
333
334    protected PublishedDocumentFactory getPublishedDocumentFactory(PublicationTreeConfigDescriptor config,
335            PublicationTreeDescriptor treeDescriptor, CoreSession coreSession, Map<String, String> params) {
336        PublishedDocumentFactoryDescriptor factoryDesc = getPublishedDocumentFactoryDescriptor(config, treeDescriptor);
337        ValidatorsRule validatorsRule = getValidatorsRule(factoryDesc);
338
339        PublishedDocumentFactory factory;
340        try {
341            factory = factoryDesc.getKlass().newInstance();
342        } catch (ReflectiveOperationException e) {
343            throw new NuxeoException("Error while creating factory " + factoryDesc.getName(), e);
344        }
345        factory.init(coreSession, validatorsRule, params);
346        return factory;
347    }
348
349    protected ValidatorsRule getValidatorsRule(PublishedDocumentFactoryDescriptor factoryDesc) {
350        String validatorsRuleName = factoryDesc.getValidatorsRuleName();
351        ValidatorsRule validatorsRule = null;
352        if (validatorsRuleName != null) {
353            ValidatorsRuleDescriptor validatorsRuleDesc = validatorsRuleDescriptors.get(validatorsRuleName);
354            if (validatorsRuleDesc == null) {
355                throw new NuxeoException("Unable to find validatorsRule" + validatorsRuleName);
356            }
357            try {
358                validatorsRule = validatorsRuleDesc.getKlass().newInstance();
359            } catch (ReflectiveOperationException e) {
360                throw new NuxeoException("Error while creating validatorsRule " + validatorsRuleName, e);
361            }
362        }
363        return validatorsRule;
364    }
365
366    protected PublishedDocumentFactoryDescriptor getPublishedDocumentFactoryDescriptor(
367            PublicationTreeConfigDescriptor config, PublicationTreeDescriptor treeDescriptor) {
368        String factoryName = config.getFactory();
369        if (factoryName == null) {
370            factoryName = treeDescriptor.getFactory();
371        }
372
373        PublishedDocumentFactoryDescriptor factoryDesc = factoryDescriptors.get(factoryName);
374        if (factoryDesc == null) {
375            throw new NuxeoException("Unable to find factory" + factoryName);
376        }
377        return factoryDesc;
378    }
379
380    protected PublicationTreeConfigDescriptor getPublicationTreeConfigDescriptor(String treeConfigName) {
381        if (!treeConfigDescriptors.containsKey(treeConfigName)) {
382            throw new NuxeoException("Unknow treeConfig :" + treeConfigName);
383        }
384        return treeConfigDescriptors.get(treeConfigName);
385    }
386
387    protected PublicationTreeDescriptor getPublicationTreeDescriptor(PublicationTreeConfigDescriptor config) {
388        String treeImplName = config.getTree();
389        if (!treeDescriptors.containsKey(treeImplName)) {
390            throw new NuxeoException("Unknow treeImplementation :" + treeImplName);
391        }
392        return treeDescriptors.get(treeImplName);
393    }
394
395    protected PublicationTree getPublicationTree(PublicationTreeDescriptor treeDescriptor, String sid,
396            CoreSession coreSession, Map<String, String> parameters, PublishedDocumentFactory factory,
397            String configName, String treeTitle) throws PublicationTreeNotAvailable {
398        PublicationTree treeImpl;
399        try {
400            treeImpl = treeDescriptor.getKlass().newInstance();
401        } catch (ReflectiveOperationException e) {
402            throw new NuxeoException("Error while creating tree implementation", e);
403        }
404        treeImpl.initTree(sid, coreSession, parameters, factory, configName, treeTitle);
405        return treeImpl;
406    }
407
408    @Override
409    public PublishedDocument publish(DocumentModel doc, PublicationNode targetNode) {
410        return publish(doc, targetNode, null);
411    }
412
413    @Override
414    public PublishedDocument publish(DocumentModel doc, PublicationNode targetNode, Map<String, String> params) {
415
416        PublicationTree tree = liveTrees.get(targetNode.getSessionId());
417        if (tree != null) {
418            return tree.publish(doc, targetNode, params);
419        } else {
420            throw new NuxeoException("Calling getChildrenNodes on a closed tree");
421        }
422    }
423
424    @Override
425    public void unpublish(DocumentModel doc, PublicationNode targetNode) {
426        PublicationTree tree = liveTrees.get(targetNode.getSessionId());
427        if (tree != null) {
428            tree.unpublish(doc, targetNode);
429        } else {
430            throw new NuxeoException("Calling getChildrenNodes on a closed tree");
431        }
432    }
433
434    @Override
435    public void unpublish(String sid, PublishedDocument publishedDocument) {
436        PublicationTree tree = liveTrees.get(sid);
437        if (tree != null) {
438            tree.unpublish(publishedDocument);
439        } else {
440            throw new NuxeoException("Calling getChildrenNodes on a closed tree");
441        }
442    }
443
444    @Override
445    public List<PublishedDocument> getChildrenDocuments(PublicationNode node) {
446
447        PublicationTree tree = liveTrees.get(node.getSessionId());
448        if (tree != null) {
449            return tree.getPublishedDocumentInNode(tree.getNodeByPath(node.getPath()));
450        } else {
451            throw new NuxeoException("Calling getChildrenDocuments on a closed tree");
452        }
453    }
454
455    protected List<PublicationNode> makeRemotable(List<PublicationNode> nodes, String sid) {
456        List<PublicationNode> remoteNodes = new ArrayList<PublicationNode>();
457
458        for (PublicationNode node : nodes) {
459            remoteNodes.add(new ProxyNode(node, sid));
460        }
461
462        return remoteNodes;
463    }
464
465    @Override
466    public List<PublicationNode> getChildrenNodes(PublicationNode node) {
467        String sid = node.getSessionId();
468        PublicationTree tree = liveTrees.get(sid);
469        if (tree != null) {
470            return makeRemotable(tree.getNodeByPath(node.getPath()).getChildrenNodes(), sid);
471        } else {
472            throw new NuxeoException("Calling getChildrenNodes on a closed tree");
473        }
474    }
475
476    @Override
477    public PublicationNode getParent(PublicationNode node) {
478        String sid = node.getSessionId();
479        PublicationTree tree = liveTrees.get(sid);
480        if (tree != null) {
481            PublicationNode liveNode;
482            liveNode = tree.getNodeByPath(node.getPath()).getParent();
483            if (liveNode == null) {
484                return null;
485            }
486            return new ProxyNode(liveNode, sid);
487        } else {
488            log.error("Calling getParent on a closed tree");
489            return null;
490        }
491    }
492
493    @Override
494    public PublicationNode getNodeByPath(String sid, String path) {
495        PublicationTree tree = liveTrees.get(sid);
496        if (tree != null) {
497            return new ProxyNode(tree.getNodeByPath(path), sid);
498        } else {
499            throw new NuxeoException("Calling getNodeByPath on a closed tree");
500        }
501    }
502
503    @Override
504    public List<PublishedDocument> getExistingPublishedDocument(String sid, DocumentLocation docLoc) {
505        PublicationTree tree = liveTrees.get(sid);
506        if (tree != null) {
507            return tree.getExistingPublishedDocument(docLoc);
508        } else {
509            throw new NuxeoException("Calling getNodeByPath on a closed tree");
510        }
511    }
512
513    @Override
514    public List<PublishedDocument> getPublishedDocumentInNode(PublicationNode node) {
515        String sid = node.getSessionId();
516        PublicationTree tree = liveTrees.get(sid);
517        if (tree != null) {
518            return tree.getPublishedDocumentInNode(tree.getNodeByPath(node.getPath()));
519        } else {
520            throw new NuxeoException("Calling getPublishedDocumentInNode on a closed tree");
521        }
522    }
523
524    @Override
525    public void setCurrentDocument(String sid, DocumentModel currentDocument) {
526        PublicationTree tree = liveTrees.get(sid);
527        if (tree != null) {
528            tree.setCurrentDocument(currentDocument);
529        } else {
530            throw new NuxeoException("Calling validatorPublishDocument on a closed tree");
531        }
532    }
533
534    @Override
535    public void validatorPublishDocument(String sid, PublishedDocument publishedDocument, String comment) {
536        PublicationTree tree = liveTrees.get(sid);
537        if (tree != null) {
538            tree.validatorPublishDocument(publishedDocument, comment);
539        } else {
540            throw new NuxeoException("Calling validatorPublishDocument on a closed tree");
541        }
542    }
543
544    @Override
545    public void validatorRejectPublication(String sid, PublishedDocument publishedDocument, String comment) {
546        PublicationTree tree = liveTrees.get(sid);
547        if (tree != null) {
548            tree.validatorRejectPublication(publishedDocument, comment);
549        } else {
550            throw new NuxeoException("Calling validatorPublishDocument on a closed tree");
551        }
552    }
553
554    @Override
555    public boolean canPublishTo(String sid, PublicationNode publicationNode) {
556        PublicationTree tree = liveTrees.get(sid);
557        if (tree != null) {
558            return tree.canPublishTo(publicationNode);
559        } else {
560            throw new NuxeoException("Calling validatorPublishDocument on a closed tree");
561        }
562    }
563
564    @Override
565    public boolean canUnpublish(String sid, PublishedDocument publishedDocument) {
566        PublicationTree tree = liveTrees.get(sid);
567        if (tree != null) {
568            return tree.canUnpublish(publishedDocument);
569        } else {
570            throw new NuxeoException("Calling validatorPublishDocument on a closed tree");
571        }
572    }
573
574    @Override
575    public boolean canManagePublishing(String sid, PublishedDocument publishedDocument) {
576        PublicationTree tree = liveTrees.get(sid);
577        if (tree != null) {
578            return tree.canManagePublishing(publishedDocument);
579        } else {
580            throw new NuxeoException("Calling validatorPublishDocument on a closed tree");
581        }
582    }
583
584    @Override
585    public boolean isPublishedDocument(DocumentModel documentModel) {
586        return PublicationRelationHelper.isPublished(documentModel);
587    }
588
589    @Override
590    public PublicationTree getPublicationTreeFor(DocumentModel doc, CoreSession coreSession) {
591        PublicationTree tree = null;
592        try {
593            tree = PublicationRelationHelper.getPublicationTreeUsedForPublishing(doc, coreSession);
594        } catch (NuxeoException e) {
595            // TODO catch proper exception
596            log.error("Unable to get PublicationTree for " + doc.getPathAsString()
597                    + ". Fallback on first PublicationTree accepting this document.", e);
598            for (String treeName : treeConfigDescriptors.keySet()) {
599                tree = getPublicationTree(treeName, coreSession, null);
600                if (tree.isPublicationNode(doc)) {
601                    break;
602                }
603            }
604        }
605        return tree;
606    }
607
608    @Override
609    public boolean hasValidationTask(String sid, PublishedDocument publishedDocument) {
610        PublicationTree tree = liveTrees.get(sid);
611        if (tree != null) {
612            return tree.hasValidationTask(publishedDocument);
613        } else {
614            throw new NuxeoException("Calling validatorPublishDocument on a closed tree");
615        }
616    }
617
618    @Override
619    public PublishedDocument wrapToPublishedDocument(String sid, DocumentModel documentModel) {
620        PublicationTree tree = liveTrees.get(sid);
621        if (tree != null) {
622            return tree.wrapToPublishedDocument(documentModel);
623        } else {
624            throw new NuxeoException("Calling validatorPublishDocument on a closed tree");
625        }
626    }
627
628    @Override
629    public boolean isPublicationNode(String sid, DocumentModel documentModel) {
630        PublicationTree tree = liveTrees.get(sid);
631        if (tree != null) {
632            return tree.isPublicationNode(documentModel);
633        } else {
634            throw new NuxeoException("Calling validatorPublishDocument on a closed tree");
635        }
636    }
637
638    @Override
639    public PublicationNode wrapToPublicationNode(String sid, DocumentModel documentModel) {
640        PublicationTree tree = liveTrees.get(sid);
641        if (tree != null) {
642            return tree.wrapToPublicationNode(documentModel);
643        } else {
644            throw new NuxeoException("Calling validatorPublishDocument on a closed tree");
645        }
646    }
647
648    @Override
649    public PublicationNode wrapToPublicationNode(DocumentModel documentModel, CoreSession coreSession)
650            throws PublicationTreeNotAvailable {
651        for (String name : getAvailablePublicationTree()) {
652            PublicationTree tree = getPublicationTree(name, coreSession, null);
653            PublicationTreeConfigDescriptor config = treeConfigDescriptors.get(tree.getConfigName());
654            if (!config.islocalSectionTree()) {
655                // ignore all non local section tree
656                continue;
657            }
658            if (tree.isPublicationNode(documentModel)) {
659                return tree.wrapToPublicationNode(documentModel);
660            }
661        }
662        return null;
663    }
664
665    protected void registerPendingDescriptors() {
666        // TODO what to do with multiple repositories?
667        RepositoryManager repositoryManager = Framework.getService(RepositoryManager.class);
668        String repositoryName = repositoryManager.getDefaultRepositoryName();
669        List<DocumentModel> domains = new DomainsFinder(repositoryName).getDomains();
670        for (DocumentModel domain : domains) {
671            registerTreeConfigFor(domain);
672        }
673    }
674
675    public void registerTreeConfigFor(DocumentModel domain) {
676        for (PublicationTreeConfigDescriptor desc : pendingDescriptors.values()) {
677            PublicationTreeConfigDescriptor newDesc = new PublicationTreeConfigDescriptor(desc);
678            String newTreeName = desc.getName() + "-" + domain.getName();
679            newDesc.setName(newTreeName);
680            Path newPath = domain.getPath();
681            Map<String, String> parameters = newDesc.getParameters();
682            newPath = newPath.append(parameters.remove(RELATIVE_ROOT_PATH_KEY));
683            parameters.put(ROOT_PATH_KEY, newPath.toString());
684            parameters.put(PublisherService.DOMAIN_NAME_KEY, domain.getTitle());
685            treeConfigDescriptors.put(newDesc.getName(), newDesc);
686        }
687    }
688
689    public void unRegisterTreeConfigFor(DocumentModel domain) {
690        unRegisterTreeConfigFor(domain.getName());
691    }
692
693    /**
694     * @since 7.3
695     */
696    public void unRegisterTreeConfigFor(String domainName) {
697        for (PublicationTreeConfigDescriptor desc : pendingDescriptors.values()) {
698            String treeName = desc.getName() + "-" + domainName;
699            treeConfigDescriptors.remove(treeName);
700            for (Iterator<String> it = liveTrees.keySet().iterator(); it.hasNext();) {
701                String entry = it.next();
702                if (entry.startsWith(treeName)) {
703                    it.remove();
704                }
705            }
706        }
707    }
708
709    @Override
710    public Map<String, String> getParametersFor(String treeConfigName) {
711        PublicationTreeConfigDescriptor desc = treeConfigDescriptors.get(treeConfigName);
712        Map<String, String> parameters = new HashMap<String, String>();
713        if (desc != null) {
714            parameters.putAll(desc.getParameters());
715        }
716        return parameters;
717    }
718
719    @Override
720    public RootSectionFinder getRootSectionFinder(CoreSession session) {
721        if (rootSectionFinderFactory != null) {
722            return rootSectionFinderFactory.getRootSectionFinder(session);
723        }
724        return new DefaultRootSectionsFinder(session);
725    }
726}