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        if (TransactionHelper.startTransaction()) {
111            boolean completedAbruptly = true;
112            try {
113                doApplicationStarted();
114                completedAbruptly = false;
115            } finally {
116                if (completedAbruptly) {
117                    TransactionHelper.setTransactionRollbackOnly();
118                }
119                TransactionHelper.commitOrRollbackTransaction();
120            }
121        } else {
122            doApplicationStarted();
123        }
124    }
125
126    protected void doApplicationStarted() {
127        ClassLoader jbossCL = Thread.currentThread().getContextClassLoader();
128        ClassLoader nuxeoCL = PublisherServiceImpl.class.getClassLoader();
129        try {
130            Thread.currentThread().setContextClassLoader(nuxeoCL);
131            log.info("Publisher Service initialization");
132            registerPendingDescriptors();
133        } finally {
134            Thread.currentThread().setContextClassLoader(jbossCL);
135            log.debug("JBoss ClassLoader restored");
136        }
137    }
138
139    @Override
140    public void activate(ComponentContext context) {
141        liveTrees = new HashMap<String, PublicationTree>();
142        treeDescriptors = new HashMap<String, PublicationTreeDescriptor>();
143        factoryDescriptors = new HashMap<String, PublishedDocumentFactoryDescriptor>();
144        treeConfigDescriptors = new HashMap<String, PublicationTreeConfigDescriptor>();
145        validatorsRuleDescriptors = new HashMap<String, ValidatorsRuleDescriptor>();
146        pendingDescriptors = new HashMap<String, PublicationTreeConfigDescriptor>();
147    }
148
149    // for testing cleanup
150    public int getLiveTreeCount() {
151        return liveTrees.size();
152    }
153
154    public PublicationTree getTreeBySid(String sid) {
155        return liveTrees.get(sid);
156    }
157
158    @Override
159    public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
160
161        log.debug("Registry contribution for EP " + extensionPoint);
162
163        if (TREE_EP.equals(extensionPoint)) {
164            PublicationTreeDescriptor desc = (PublicationTreeDescriptor) contribution;
165            treeDescriptors.put(desc.getName(), desc);
166        } else if (TREE_CONFIG_EP.equals(extensionPoint)) {
167            PublicationTreeConfigDescriptor desc = (PublicationTreeConfigDescriptor) contribution;
168            registerTreeConfig(desc);
169        } else if (FACTORY_EP.equals(extensionPoint)) {
170            PublishedDocumentFactoryDescriptor desc = (PublishedDocumentFactoryDescriptor) contribution;
171            factoryDescriptors.put(desc.getName(), desc);
172        } else if (VALIDATORS_RULE_EP.equals(extensionPoint)) {
173            ValidatorsRuleDescriptor desc = (ValidatorsRuleDescriptor) contribution;
174            validatorsRuleDescriptors.put(desc.getName(), desc);
175        } else if (ROOT_SECTION_FINDER_FACTORY_EP.equals(extensionPoint)) {
176            RootSectionFinderFactoryDescriptor desc = (RootSectionFinderFactoryDescriptor) contribution;
177            try {
178                rootSectionFinderFactory = desc.getFactory().newInstance();
179            } catch (ReflectiveOperationException t) {
180                log.error("Unable to load custom RootSectionFinderFactory", t);
181            }
182        }
183    }
184
185    protected void registerTreeConfig(PublicationTreeConfigDescriptor desc) {
186        if (desc.getParameters().get("RelativeRootPath") != null) {
187            pendingDescriptors.put(desc.getName(), desc);
188        } else {
189            treeConfigDescriptors.put(desc.getName(), desc);
190        }
191    }
192
193    @Override
194    public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
195        if (contribution instanceof PublicationTreeDescriptor) {
196            treeDescriptors.remove(((PublicationTreeDescriptor) contribution).getName());
197        } else if (contribution instanceof PublicationTreeConfigDescriptor) {
198            String name = ((PublicationTreeConfigDescriptor) contribution).getName();
199            pendingDescriptors.remove(name);
200            treeConfigDescriptors.remove(name);
201        } else if (contribution instanceof ValidatorsRuleDescriptor) {
202            validatorsRuleDescriptors.remove(((ValidatorsRuleDescriptor) contribution).getName());
203        } else if (contribution instanceof RootSectionFinderFactoryDescriptor) {
204            rootSectionFinderFactory = null;
205        }
206    }
207
208    protected String computeTreeSessionId(String treeConfigName, CoreSession coreSession) {
209        return computeTreeSessionId(treeConfigName, coreSession.getSessionId());
210    }
211
212    protected String computeTreeSessionId(String treeConfigName, String sid) {
213        return treeConfigName + sid;
214    }
215
216    @Override
217    public List<String> getAvailablePublicationTree() {
218        List<String> treeConfigs = new ArrayList<String>();
219        treeConfigs.addAll(treeConfigDescriptors.keySet());
220        return treeConfigs;
221    }
222
223    @Override
224    public Map<String, String> getAvailablePublicationTrees() {
225        Map<String, String> trees = new HashMap<String, String>();
226        for (PublicationTreeConfigDescriptor desc : treeConfigDescriptors.values()) {
227            String title = desc.getTitle() == null ? desc.getName() : desc.getTitle();
228            trees.put(desc.getName(), title);
229        }
230        return trees;
231    }
232
233    @Override
234    public PublicationTree getPublicationTree(String treeName, CoreSession coreSession, Map<String, String> params)
235            throws PublicationTreeNotAvailable {
236        return getPublicationTree(treeName, coreSession, params, null);
237    }
238
239    @Override
240    public PublicationTree getPublicationTree(String treeName, CoreSession coreSession, Map<String, String> params,
241            DocumentModel currentDocument) throws PublicationTreeNotAvailable {
242        PublicationTree tree = getOrBuildTree(treeName, coreSession, params);
243        if (tree == null) {
244            return null;
245        }
246        if (currentDocument != null) {
247            tree.setCurrentDocument(currentDocument);
248        }
249        return new ProxyTree(tree, tree.getSessionId());
250    }
251
252    @Override
253    public Map<String, String> initRemoteSession(String treeConfigName, Map<String, String> params) {
254        CoreSession coreSession = CoreInstance.openCoreSession(null);
255        PublicationTree tree = getPublicationTree(treeConfigName, coreSession, params);
256
257        remoteLiveTrees.put(tree.getSessionId(), coreSession.getSessionId());
258
259        Map<String, String> res = new HashMap<String, String>();
260        res.put("sessionId", tree.getSessionId());
261        res.put("title", tree.getTitle());
262        res.put("nodeType", tree.getNodeType());
263        res.put("treeName", tree.getConfigName());
264        res.put("path", tree.getPath());
265
266        return res;
267    }
268
269    @Override
270    public void release(String sid) {
271        PublicationTree tree;
272
273        if (liveTrees.containsKey(sid)) {
274            tree = liveTrees.get(sid);
275            tree.release();
276            liveTrees.remove(sid);
277        }
278        if (remoteLiveTrees.containsKey(sid)) {
279            // close here session opened for remote trees
280            String sessionId = remoteLiveTrees.get(sid);
281            CoreSession remoteSession = CoreInstance.getInstance().getSession(sessionId);
282            remoteSession.close();
283            remoteLiveTrees.remove(sid);
284        }
285    }
286
287    @Override
288    public void releaseAllTrees(String sessionId) {
289        for (String configName : treeConfigDescriptors.keySet()) {
290            String treeid = computeTreeSessionId(configName, sessionId);
291            release(treeid);
292        }
293    }
294
295    protected PublicationTree getOrBuildTree(String treeConfigName, CoreSession coreSession, Map<String, String> params)
296            throws PublicationTreeNotAvailable {
297        String key = computeTreeSessionId(treeConfigName, coreSession);
298        PublicationTree tree;
299        if (liveTrees.containsKey(key)) {
300            tree = liveTrees.get(key);
301        } else {
302            tree = buildTree(key, treeConfigName, coreSession, params);
303            if (tree != null) {
304                liveTrees.put(key, tree);
305            }
306        }
307        return tree;
308    }
309
310    protected PublicationTree buildTree(String sid, String treeConfigName, CoreSession coreSession,
311            Map<String, String> params) throws PublicationTreeNotAvailable {
312        PublicationTreeConfigDescriptor config = getPublicationTreeConfigDescriptor(treeConfigName);
313        Map<String, String> allParameters = computeAllParameters(config, params);
314        PublicationTreeDescriptor treeDescriptor = getPublicationTreeDescriptor(config);
315        PublishedDocumentFactory publishedDocumentFactory = getPublishedDocumentFactory(config, treeDescriptor,
316                coreSession, allParameters);
317        return getPublicationTree(treeDescriptor, sid, coreSession, allParameters, publishedDocumentFactory,
318                config.getName(), config.getTitle());
319    }
320
321    protected Map<String, String> computeAllParameters(PublicationTreeConfigDescriptor config,
322            Map<String, String> params) {
323        final Map<String, String> allParameters = config.getParameters();
324        if (params != null) {
325            allParameters.putAll(params);
326        }
327        return allParameters;
328    }
329
330    protected PublishedDocumentFactory getPublishedDocumentFactory(PublicationTreeConfigDescriptor config,
331            PublicationTreeDescriptor treeDescriptor, CoreSession coreSession, Map<String, String> params) {
332        PublishedDocumentFactoryDescriptor factoryDesc = getPublishedDocumentFactoryDescriptor(config, treeDescriptor);
333        ValidatorsRule validatorsRule = getValidatorsRule(factoryDesc);
334
335        PublishedDocumentFactory factory;
336        try {
337            factory = factoryDesc.getKlass().newInstance();
338        } catch (ReflectiveOperationException e) {
339            throw new NuxeoException("Error while creating factory " + factoryDesc.getName(), e);
340        }
341        factory.init(coreSession, validatorsRule, params);
342        return factory;
343    }
344
345    protected ValidatorsRule getValidatorsRule(PublishedDocumentFactoryDescriptor factoryDesc) {
346        String validatorsRuleName = factoryDesc.getValidatorsRuleName();
347        ValidatorsRule validatorsRule = null;
348        if (validatorsRuleName != null) {
349            ValidatorsRuleDescriptor validatorsRuleDesc = validatorsRuleDescriptors.get(validatorsRuleName);
350            if (validatorsRuleDesc == null) {
351                throw new NuxeoException("Unable to find validatorsRule" + validatorsRuleName);
352            }
353            try {
354                validatorsRule = validatorsRuleDesc.getKlass().newInstance();
355            } catch (ReflectiveOperationException e) {
356                throw new NuxeoException("Error while creating validatorsRule " + validatorsRuleName, e);
357            }
358        }
359        return validatorsRule;
360    }
361
362    protected PublishedDocumentFactoryDescriptor getPublishedDocumentFactoryDescriptor(
363            PublicationTreeConfigDescriptor config, PublicationTreeDescriptor treeDescriptor) {
364        String factoryName = config.getFactory();
365        if (factoryName == null) {
366            factoryName = treeDescriptor.getFactory();
367        }
368
369        PublishedDocumentFactoryDescriptor factoryDesc = factoryDescriptors.get(factoryName);
370        if (factoryDesc == null) {
371            throw new NuxeoException("Unable to find factory" + factoryName);
372        }
373        return factoryDesc;
374    }
375
376    protected PublicationTreeConfigDescriptor getPublicationTreeConfigDescriptor(String treeConfigName) {
377        if (!treeConfigDescriptors.containsKey(treeConfigName)) {
378            throw new NuxeoException("Unknow treeConfig :" + treeConfigName);
379        }
380        return treeConfigDescriptors.get(treeConfigName);
381    }
382
383    protected PublicationTreeDescriptor getPublicationTreeDescriptor(PublicationTreeConfigDescriptor config) {
384        String treeImplName = config.getTree();
385        if (!treeDescriptors.containsKey(treeImplName)) {
386            throw new NuxeoException("Unknow treeImplementation :" + treeImplName);
387        }
388        return treeDescriptors.get(treeImplName);
389    }
390
391    protected PublicationTree getPublicationTree(PublicationTreeDescriptor treeDescriptor, String sid,
392            CoreSession coreSession, Map<String, String> parameters, PublishedDocumentFactory factory,
393            String configName, String treeTitle) throws PublicationTreeNotAvailable {
394        PublicationTree treeImpl;
395        try {
396            treeImpl = treeDescriptor.getKlass().newInstance();
397        } catch (ReflectiveOperationException e) {
398            throw new NuxeoException("Error while creating tree implementation", e);
399        }
400        treeImpl.initTree(sid, coreSession, parameters, factory, configName, treeTitle);
401        return treeImpl;
402    }
403
404    @Override
405    public PublishedDocument publish(DocumentModel doc, PublicationNode targetNode) {
406        return publish(doc, targetNode, null);
407    }
408
409    @Override
410    public PublishedDocument publish(DocumentModel doc, PublicationNode targetNode, Map<String, String> params)
411            {
412
413        PublicationTree tree = liveTrees.get(targetNode.getSessionId());
414        if (tree != null) {
415            return tree.publish(doc, targetNode, params);
416        } else {
417            throw new NuxeoException("Calling getChildrenNodes on a closed tree");
418        }
419    }
420
421    @Override
422    public void unpublish(DocumentModel doc, PublicationNode targetNode) {
423        PublicationTree tree = liveTrees.get(targetNode.getSessionId());
424        if (tree != null) {
425            tree.unpublish(doc, targetNode);
426        } else {
427            throw new NuxeoException("Calling getChildrenNodes on a closed tree");
428        }
429    }
430
431    @Override
432    public void unpublish(String sid, PublishedDocument publishedDocument) {
433        PublicationTree tree = liveTrees.get(sid);
434        if (tree != null) {
435            tree.unpublish(publishedDocument);
436        } else {
437            throw new NuxeoException("Calling getChildrenNodes on a closed tree");
438        }
439    }
440
441    @Override
442    public List<PublishedDocument> getChildrenDocuments(PublicationNode node) {
443
444        PublicationTree tree = liveTrees.get(node.getSessionId());
445        if (tree != null) {
446            return tree.getPublishedDocumentInNode(tree.getNodeByPath(node.getPath()));
447        } else {
448            throw new NuxeoException("Calling getChildrenDocuments on a closed tree");
449        }
450    }
451
452    protected List<PublicationNode> makeRemotable(List<PublicationNode> nodes, String sid) {
453        List<PublicationNode> remoteNodes = new ArrayList<PublicationNode>();
454
455        for (PublicationNode node : nodes) {
456            remoteNodes.add(new ProxyNode(node, sid));
457        }
458
459        return remoteNodes;
460    }
461
462    @Override
463    public List<PublicationNode> getChildrenNodes(PublicationNode node) {
464        String sid = node.getSessionId();
465        PublicationTree tree = liveTrees.get(sid);
466        if (tree != null) {
467            return makeRemotable(tree.getNodeByPath(node.getPath()).getChildrenNodes(), sid);
468        } else {
469            throw new NuxeoException("Calling getChildrenNodes on a closed tree");
470        }
471    }
472
473    @Override
474    public PublicationNode getParent(PublicationNode node) {
475        String sid = node.getSessionId();
476        PublicationTree tree = liveTrees.get(sid);
477        if (tree != null) {
478            PublicationNode liveNode;
479            liveNode = tree.getNodeByPath(node.getPath()).getParent();
480            if (liveNode == null) {
481                return null;
482            }
483            return new ProxyNode(liveNode, sid);
484        } else {
485            log.error("Calling getParent on a closed tree");
486            return null;
487        }
488    }
489
490    @Override
491    public PublicationNode getNodeByPath(String sid, String path) {
492        PublicationTree tree = liveTrees.get(sid);
493        if (tree != null) {
494            return new ProxyNode(tree.getNodeByPath(path), sid);
495        } else {
496            throw new NuxeoException("Calling getNodeByPath on a closed tree");
497        }
498    }
499
500    @Override
501    public List<PublishedDocument> getExistingPublishedDocument(String sid, DocumentLocation docLoc)
502            {
503        PublicationTree tree = liveTrees.get(sid);
504        if (tree != null) {
505            return tree.getExistingPublishedDocument(docLoc);
506        } else {
507            throw new NuxeoException("Calling getNodeByPath on a closed tree");
508        }
509    }
510
511    @Override
512    public List<PublishedDocument> getPublishedDocumentInNode(PublicationNode node) {
513        String sid = node.getSessionId();
514        PublicationTree tree = liveTrees.get(sid);
515        if (tree != null) {
516            return tree.getPublishedDocumentInNode(tree.getNodeByPath(node.getPath()));
517        } else {
518            throw new NuxeoException("Calling getPublishedDocumentInNode on a closed tree");
519        }
520    }
521
522    @Override
523    public void setCurrentDocument(String sid, DocumentModel currentDocument) {
524        PublicationTree tree = liveTrees.get(sid);
525        if (tree != null) {
526            tree.setCurrentDocument(currentDocument);
527        } else {
528            throw new NuxeoException("Calling validatorPublishDocument on a closed tree");
529        }
530    }
531
532    @Override
533    public void validatorPublishDocument(String sid, PublishedDocument publishedDocument, String comment)
534            {
535        PublicationTree tree = liveTrees.get(sid);
536        if (tree != null) {
537            tree.validatorPublishDocument(publishedDocument, comment);
538        } else {
539            throw new NuxeoException("Calling validatorPublishDocument on a closed tree");
540        }
541    }
542
543    @Override
544    public void validatorRejectPublication(String sid, PublishedDocument publishedDocument, String comment)
545            {
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}