/*
* (C) Copyright 2006-2009 Nuxeo SA (http://nuxeo.com/) and others.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Contributors:
* Nuxeo
*/
package org.nuxeo.ecm.platform.publisher.impl.service;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuxeo.common.utils.Path;
import org.nuxeo.ecm.core.api.CoreSession;
import org.nuxeo.ecm.core.api.DocumentModel;
import org.nuxeo.ecm.core.api.NuxeoException;
import org.nuxeo.ecm.core.api.repository.RepositoryManager;
import org.nuxeo.ecm.core.repository.RepositoryService;
import org.nuxeo.ecm.platform.publisher.api.PublicationNode;
import org.nuxeo.ecm.platform.publisher.api.PublicationTree;
import org.nuxeo.ecm.platform.publisher.api.PublishedDocument;
import org.nuxeo.ecm.platform.publisher.api.PublishedDocumentFactory;
import org.nuxeo.ecm.platform.publisher.api.PublisherService;
import org.nuxeo.ecm.platform.publisher.descriptors.PublicationTreeConfigDescriptor;
import org.nuxeo.ecm.platform.publisher.descriptors.PublicationTreeDescriptor;
import org.nuxeo.ecm.platform.publisher.descriptors.PublishedDocumentFactoryDescriptor;
import org.nuxeo.ecm.platform.publisher.descriptors.RootSectionFinderFactoryDescriptor;
import org.nuxeo.ecm.platform.publisher.helper.PublicationRelationHelper;
import org.nuxeo.ecm.platform.publisher.helper.RootSectionFinder;
import org.nuxeo.ecm.platform.publisher.helper.RootSectionFinderFactory;
import org.nuxeo.ecm.platform.publisher.impl.finder.DefaultRootSectionsFinder;
import org.nuxeo.ecm.platform.publisher.rules.ValidatorsRule;
import org.nuxeo.ecm.platform.publisher.rules.ValidatorsRuleDescriptor;
import org.nuxeo.runtime.api.Framework;
import org.nuxeo.runtime.model.ComponentContext;
import org.nuxeo.runtime.model.ComponentInstance;
import org.nuxeo.runtime.model.DefaultComponent;
import org.nuxeo.runtime.transaction.TransactionHelper;
/**
* POJO implementation of the publisher service.
*
* @author tiry
*/
public class PublisherServiceImpl extends DefaultComponent implements PublisherService {
private final Log log = LogFactory.getLog(PublisherServiceImpl.class);
protected Map<String, PublicationTreeDescriptor> treeDescriptors = new HashMap<String, PublicationTreeDescriptor>();
protected Map<String, PublishedDocumentFactoryDescriptor> factoryDescriptors = new HashMap<String, PublishedDocumentFactoryDescriptor>();
protected Map<String, PublicationTreeConfigDescriptor> treeConfigDescriptors = new HashMap<String, PublicationTreeConfigDescriptor>();
protected Map<String, ValidatorsRuleDescriptor> validatorsRuleDescriptors = new HashMap<String, ValidatorsRuleDescriptor>();
protected Map<String, PublicationTreeConfigDescriptor> pendingDescriptors = new HashMap<String, PublicationTreeConfigDescriptor>();
protected RootSectionFinderFactory rootSectionFinderFactory = null;
public static final String TREE_EP = "tree";
public static final String TREE_CONFIG_EP = "treeInstance";
public static final String VALIDATORS_RULE_EP = "validatorsRule";
public static final String FACTORY_EP = "factory";
public static final String ROOT_SECTION_FINDER_FACTORY_EP = "rootSectionFinderFactory";
protected static final String ROOT_PATH_KEY = "RootPath";
protected static final String RELATIVE_ROOT_PATH_KEY = "RelativeRootPath";
@Override
public void applicationStarted(ComponentContext context) {
RepositoryService repositoryService = Framework.getService(RepositoryService.class);
if (repositoryService == null) {
// RepositoryService failed to start, no need to go further
return;
}
boolean txWasStartedOutsideComponent = TransactionHelper.isTransactionActiveOrMarkedRollback();
if (txWasStartedOutsideComponent || TransactionHelper.startTransaction()) {
boolean completedAbruptly = true;
try {
doApplicationStarted();
completedAbruptly = false;
} finally {
if (completedAbruptly) {
TransactionHelper.setTransactionRollbackOnly();
}
if (!txWasStartedOutsideComponent) {
TransactionHelper.commitOrRollbackTransaction();
}
}
} else {
doApplicationStarted();
}
}
protected void doApplicationStarted() {
ClassLoader jbossCL = Thread.currentThread().getContextClassLoader();
ClassLoader nuxeoCL = PublisherServiceImpl.class.getClassLoader();
try {
Thread.currentThread().setContextClassLoader(nuxeoCL);
log.info("Publisher Service initialization");
registerPendingDescriptors();
} finally {
Thread.currentThread().setContextClassLoader(jbossCL);
log.debug("JBoss ClassLoader restored");
}
}
@Override
public void activate(ComponentContext context) {
treeDescriptors = new HashMap<String, PublicationTreeDescriptor>();
factoryDescriptors = new HashMap<String, PublishedDocumentFactoryDescriptor>();
treeConfigDescriptors = new HashMap<String, PublicationTreeConfigDescriptor>();
validatorsRuleDescriptors = new HashMap<String, ValidatorsRuleDescriptor>();
pendingDescriptors = new HashMap<String, PublicationTreeConfigDescriptor>();
}
@Override
public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
log.debug("Registry contribution for EP " + extensionPoint);
if (TREE_EP.equals(extensionPoint)) {
PublicationTreeDescriptor desc = (PublicationTreeDescriptor) contribution;
treeDescriptors.put(desc.getName(), desc);
} else if (TREE_CONFIG_EP.equals(extensionPoint)) {
PublicationTreeConfigDescriptor desc = (PublicationTreeConfigDescriptor) contribution;
registerTreeConfig(desc);
} else if (FACTORY_EP.equals(extensionPoint)) {
PublishedDocumentFactoryDescriptor desc = (PublishedDocumentFactoryDescriptor) contribution;
factoryDescriptors.put(desc.getName(), desc);
} else if (VALIDATORS_RULE_EP.equals(extensionPoint)) {
ValidatorsRuleDescriptor desc = (ValidatorsRuleDescriptor) contribution;
validatorsRuleDescriptors.put(desc.getName(), desc);
} else if (ROOT_SECTION_FINDER_FACTORY_EP.equals(extensionPoint)) {
RootSectionFinderFactoryDescriptor desc = (RootSectionFinderFactoryDescriptor) contribution;
try {
rootSectionFinderFactory = desc.getFactory().newInstance();
} catch (ReflectiveOperationException t) {
log.error("Unable to load custom RootSectionFinderFactory", t);
}
}
}
protected void registerTreeConfig(PublicationTreeConfigDescriptor desc) {
if (desc.getParameters().get("RelativeRootPath") != null) {
pendingDescriptors.put(desc.getName(), desc);
} else {
treeConfigDescriptors.put(desc.getName(), desc);
}
}
@Override
public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
if (contribution instanceof PublicationTreeDescriptor) {
treeDescriptors.remove(((PublicationTreeDescriptor) contribution).getName());
} else if (contribution instanceof PublicationTreeConfigDescriptor) {
String name = ((PublicationTreeConfigDescriptor) contribution).getName();
pendingDescriptors.remove(name);
treeConfigDescriptors.remove(name);
} else if (contribution instanceof ValidatorsRuleDescriptor) {
validatorsRuleDescriptors.remove(((ValidatorsRuleDescriptor) contribution).getName());
} else if (contribution instanceof RootSectionFinderFactoryDescriptor) {
rootSectionFinderFactory = null;
}
}
@Override
public List<String> getAvailablePublicationTree() {
List<String> treeConfigs = new ArrayList<String>();
treeConfigs.addAll(treeConfigDescriptors.keySet());
return treeConfigs;
}
@Override
public Map<String, String> getAvailablePublicationTrees() {
Map<String, String> trees = new HashMap<String, String>();
for (PublicationTreeConfigDescriptor desc : treeConfigDescriptors.values()) {
String title = desc.getTitle() == null ? desc.getName() : desc.getTitle();
trees.put(desc.getName(), title);
}
return trees;
}
@Override
public PublicationTree getPublicationTree(String treeName, CoreSession coreSession, Map<String, String> params) {
return getPublicationTree(treeName, coreSession, params, null);
}
@Override
public PublicationTree getPublicationTree(String treeName, CoreSession coreSession, Map<String, String> params,
DocumentModel currentDocument) {
PublicationTree tree = buildTree(treeName, coreSession, params);
if (tree == null) {
return null;
}
if (currentDocument != null) {
tree.setCurrentDocument(currentDocument);
}
return tree;
}
protected PublicationTree buildTree(String treeConfigName, CoreSession coreSession, Map<String, String> params) {
PublicationTreeConfigDescriptor config = getPublicationTreeConfigDescriptor(treeConfigName);
Map<String, String> allParameters = computeAllParameters(config, params);
PublicationTreeDescriptor treeDescriptor = getPublicationTreeDescriptor(config);
PublishedDocumentFactory publishedDocumentFactory = getPublishedDocumentFactory(config, treeDescriptor,
coreSession, allParameters);
return getPublicationTree(treeDescriptor, coreSession, allParameters, publishedDocumentFactory,
config.getName(), config.getTitle());
}
protected Map<String, String> computeAllParameters(PublicationTreeConfigDescriptor config,
Map<String, String> params) {
final Map<String, String> allParameters = config.getParameters();
if (params != null) {
allParameters.putAll(params);
}
return allParameters;
}
protected PublishedDocumentFactory getPublishedDocumentFactory(PublicationTreeConfigDescriptor config,
PublicationTreeDescriptor treeDescriptor, CoreSession coreSession, Map<String, String> params) {
PublishedDocumentFactoryDescriptor factoryDesc = getPublishedDocumentFactoryDescriptor(config, treeDescriptor);
ValidatorsRule validatorsRule = getValidatorsRule(factoryDesc);
PublishedDocumentFactory factory;
try {
factory = factoryDesc.getKlass().newInstance();
} catch (ReflectiveOperationException e) {
throw new NuxeoException("Error while creating factory " + factoryDesc.getName(), e);
}
factory.init(coreSession, validatorsRule, params);
return factory;
}
protected ValidatorsRule getValidatorsRule(PublishedDocumentFactoryDescriptor factoryDesc) {
String validatorsRuleName = factoryDesc.getValidatorsRuleName();
ValidatorsRule validatorsRule = null;
if (validatorsRuleName != null) {
ValidatorsRuleDescriptor validatorsRuleDesc = validatorsRuleDescriptors.get(validatorsRuleName);
if (validatorsRuleDesc == null) {
throw new NuxeoException("Unable to find validatorsRule" + validatorsRuleName);
}
try {
validatorsRule = validatorsRuleDesc.getKlass().newInstance();
} catch (ReflectiveOperationException e) {
throw new NuxeoException("Error while creating validatorsRule " + validatorsRuleName, e);
}
}
return validatorsRule;
}
protected PublishedDocumentFactoryDescriptor getPublishedDocumentFactoryDescriptor(
PublicationTreeConfigDescriptor config, PublicationTreeDescriptor treeDescriptor) {
String factoryName = config.getFactory();
if (factoryName == null) {
factoryName = treeDescriptor.getFactory();
}
PublishedDocumentFactoryDescriptor factoryDesc = factoryDescriptors.get(factoryName);
if (factoryDesc == null) {
throw new NuxeoException("Unable to find factory" + factoryName);
}
return factoryDesc;
}
protected PublicationTreeConfigDescriptor getPublicationTreeConfigDescriptor(String treeConfigName) {
if (!treeConfigDescriptors.containsKey(treeConfigName)) {
throw new NuxeoException("Unknow treeConfig :" + treeConfigName);
}
return treeConfigDescriptors.get(treeConfigName);
}
protected PublicationTreeDescriptor getPublicationTreeDescriptor(PublicationTreeConfigDescriptor config) {
String treeImplName = config.getTree();
if (!treeDescriptors.containsKey(treeImplName)) {
throw new NuxeoException("Unknow treeImplementation :" + treeImplName);
}
return treeDescriptors.get(treeImplName);
}
protected PublicationTree getPublicationTree(PublicationTreeDescriptor treeDescriptor, CoreSession coreSession,
Map<String, String> parameters, PublishedDocumentFactory factory, String configName, String treeTitle) {
PublicationTree treeImpl;
try {
treeImpl = treeDescriptor.getKlass().newInstance();
} catch (ReflectiveOperationException e) {
throw new NuxeoException("Error while creating tree implementation", e);
}
treeImpl.initTree(coreSession, parameters, factory, configName, treeTitle);
return treeImpl;
}
@Override
public PublishedDocument publish(DocumentModel doc, PublicationNode targetNode) {
return publish(doc, targetNode, null);
}
@Override
public PublishedDocument publish(DocumentModel doc, PublicationNode targetNode, Map<String, String> params) {
return targetNode.getTree().publish(doc, targetNode, params);
}
@Override
public void unpublish(DocumentModel doc, PublicationNode targetNode) {
targetNode.getTree().unpublish(doc, targetNode);
}
@Override
public boolean isPublishedDocument(DocumentModel documentModel) {
return PublicationRelationHelper.isPublished(documentModel);
}
@Override
public PublicationTree getPublicationTreeFor(DocumentModel doc, CoreSession coreSession) {
PublicationTree tree = null;
try {
tree = PublicationRelationHelper.getPublicationTreeUsedForPublishing(doc, coreSession);
} catch (NuxeoException e) {
// TODO catch proper exception
log.error("Unable to get PublicationTree for " + doc.getPathAsString()
+ ". Fallback on first PublicationTree accepting this document.", e);
for (String treeName : treeConfigDescriptors.keySet()) {
tree = getPublicationTree(treeName, coreSession, null);
if (tree.isPublicationNode(doc)) {
break;
}
}
}
return tree;
}
@Override
public PublicationNode wrapToPublicationNode(DocumentModel documentModel, CoreSession coreSession) {
for (String name : getAvailablePublicationTree()) {
PublicationTree tree = getPublicationTree(name, coreSession, null);
if (tree.isPublicationNode(documentModel)) {
return tree.wrapToPublicationNode(documentModel);
}
}
return null;
}
protected void registerPendingDescriptors() {
// TODO what to do with multiple repositories?
RepositoryManager repositoryManager = Framework.getService(RepositoryManager.class);
String repositoryName = repositoryManager.getDefaultRepositoryName();
List<DocumentModel> domains = new DomainsFinder(repositoryName).getDomains();
for (DocumentModel domain : domains) {
registerTreeConfigFor(domain);
}
}
public void registerTreeConfigFor(DocumentModel domain) {
for (PublicationTreeConfigDescriptor desc : pendingDescriptors.values()) {
PublicationTreeConfigDescriptor newDesc = new PublicationTreeConfigDescriptor(desc);
String newTreeName = desc.getName() + "-" + domain.getName();
newDesc.setName(newTreeName);
Path newPath = domain.getPath();
Map<String, String> parameters = newDesc.getParameters();
newPath = newPath.append(parameters.remove(RELATIVE_ROOT_PATH_KEY));
parameters.put(ROOT_PATH_KEY, newPath.toString());
parameters.put(PublisherService.DOMAIN_NAME_KEY, domain.getTitle());
treeConfigDescriptors.put(newDesc.getName(), newDesc);
}
}
public void unRegisterTreeConfigFor(DocumentModel domain) {
unRegisterTreeConfigFor(domain.getName());
}
/**
* @since 7.3
*/
public void unRegisterTreeConfigFor(String domainName) {
for (PublicationTreeConfigDescriptor desc : pendingDescriptors.values()) {
String treeName = desc.getName() + "-" + domainName;
treeConfigDescriptors.remove(treeName);
}
}
@Override
public Map<String, String> getParametersFor(String treeConfigName) {
PublicationTreeConfigDescriptor desc = treeConfigDescriptors.get(treeConfigName);
Map<String, String> parameters = new HashMap<String, String>();
if (desc != null) {
parameters.putAll(desc.getParameters());
}
return parameters;
}
@Override
public RootSectionFinder getRootSectionFinder(CoreSession session) {
if (rootSectionFinderFactory != null) {
return rootSectionFinderFactory.getRootSectionFinder(session);
}
return new DefaultRootSectionsFinder(session);
}
}