001/* 002 * (C) Copyright 2006-2017 Nuxeo (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 */ 019package org.nuxeo.ecm.platform.publisher.impl.service; 020 021import java.util.ArrayList; 022import java.util.HashMap; 023import java.util.List; 024import java.util.Map; 025 026import org.apache.commons.logging.Log; 027import org.apache.commons.logging.LogFactory; 028import org.nuxeo.common.utils.Path; 029import org.nuxeo.ecm.core.api.CoreSession; 030import org.nuxeo.ecm.core.api.DocumentModel; 031import org.nuxeo.ecm.core.api.NuxeoException; 032import org.nuxeo.ecm.core.api.repository.RepositoryManager; 033import org.nuxeo.ecm.core.repository.RepositoryService; 034import org.nuxeo.ecm.platform.publisher.api.PublicationNode; 035import org.nuxeo.ecm.platform.publisher.api.PublicationTree; 036import org.nuxeo.ecm.platform.publisher.api.PublishedDocument; 037import org.nuxeo.ecm.platform.publisher.api.PublishedDocumentFactory; 038import org.nuxeo.ecm.platform.publisher.api.PublisherService; 039import org.nuxeo.ecm.platform.publisher.descriptors.PublicationTreeConfigDescriptor; 040import org.nuxeo.ecm.platform.publisher.descriptors.PublicationTreeDescriptor; 041import org.nuxeo.ecm.platform.publisher.descriptors.PublishedDocumentFactoryDescriptor; 042import org.nuxeo.ecm.platform.publisher.descriptors.RootSectionFinderFactoryDescriptor; 043import org.nuxeo.ecm.platform.publisher.helper.PublicationRelationHelper; 044import org.nuxeo.ecm.platform.publisher.helper.RootSectionFinder; 045import org.nuxeo.ecm.platform.publisher.helper.RootSectionFinderFactory; 046import org.nuxeo.ecm.platform.publisher.impl.finder.DefaultRootSectionsFinder; 047import org.nuxeo.ecm.platform.publisher.rules.ValidatorsRule; 048import org.nuxeo.ecm.platform.publisher.rules.ValidatorsRuleDescriptor; 049import org.nuxeo.runtime.api.Framework; 050import org.nuxeo.runtime.model.ComponentContext; 051import org.nuxeo.runtime.model.ComponentInstance; 052import org.nuxeo.runtime.model.DefaultComponent; 053import org.nuxeo.runtime.transaction.TransactionHelper; 054 055/** 056 * POJO implementation of the publisher service. 057 * 058 * @author tiry 059 */ 060public class PublisherServiceImpl extends DefaultComponent implements PublisherService { 061 062 private final Log log = LogFactory.getLog(PublisherServiceImpl.class); 063 064 protected Map<String, PublicationTreeDescriptor> treeDescriptors = new HashMap<>(); 065 066 protected Map<String, PublishedDocumentFactoryDescriptor> factoryDescriptors = new HashMap<>(); 067 068 protected Map<String, PublicationTreeConfigDescriptor> treeConfigDescriptors = new HashMap<>(); 069 070 protected Map<String, ValidatorsRuleDescriptor> validatorsRuleDescriptors = new HashMap<>(); 071 072 protected Map<String, PublicationTreeConfigDescriptor> pendingDescriptors = new HashMap<>(); 073 074 protected RootSectionFinderFactory rootSectionFinderFactory = null; 075 076 public static final String TREE_EP = "tree"; 077 078 public static final String TREE_CONFIG_EP = "treeInstance"; 079 080 public static final String VALIDATORS_RULE_EP = "validatorsRule"; 081 082 public static final String FACTORY_EP = "factory"; 083 084 public static final String ROOT_SECTION_FINDER_FACTORY_EP = "rootSectionFinderFactory"; 085 086 protected static final String ROOT_PATH_KEY = "RootPath"; 087 088 protected static final String RELATIVE_ROOT_PATH_KEY = "RelativeRootPath"; 089 090 @Override 091 public void start(ComponentContext context) { 092 RepositoryService repositoryService = Framework.getService(RepositoryService.class); 093 if (repositoryService == null) { 094 // RepositoryService failed to start, no need to go further 095 return; 096 } 097 boolean txWasStartedOutsideComponent = TransactionHelper.isTransactionActiveOrMarkedRollback(); 098 099 if (txWasStartedOutsideComponent || TransactionHelper.startTransaction()) { 100 boolean completedAbruptly = true; 101 try { 102 doApplicationStarted(); 103 completedAbruptly = false; 104 } finally { 105 if (completedAbruptly) { 106 TransactionHelper.setTransactionRollbackOnly(); 107 } 108 if (!txWasStartedOutsideComponent) { 109 TransactionHelper.commitOrRollbackTransaction(); 110 } 111 } 112 } else { 113 doApplicationStarted(); 114 } 115 } 116 117 protected void doApplicationStarted() { 118 ClassLoader jbossCL = Thread.currentThread().getContextClassLoader(); 119 ClassLoader nuxeoCL = PublisherServiceImpl.class.getClassLoader(); 120 try { 121 Thread.currentThread().setContextClassLoader(nuxeoCL); 122 log.info("Publisher Service initialization"); 123 registerPendingDescriptors(); 124 } finally { 125 Thread.currentThread().setContextClassLoader(jbossCL); 126 log.debug("JBoss ClassLoader restored"); 127 } 128 } 129 130 @Override 131 public void activate(ComponentContext context) { 132 treeDescriptors = new HashMap<>(); 133 factoryDescriptors = new HashMap<>(); 134 treeConfigDescriptors = new HashMap<>(); 135 validatorsRuleDescriptors = new HashMap<>(); 136 pendingDescriptors = new HashMap<>(); 137 } 138 139 @Override 140 public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 141 142 log.debug("Registry contribution for EP " + extensionPoint); 143 144 if (TREE_EP.equals(extensionPoint)) { 145 PublicationTreeDescriptor desc = (PublicationTreeDescriptor) contribution; 146 treeDescriptors.put(desc.getName(), desc); 147 } else if (TREE_CONFIG_EP.equals(extensionPoint)) { 148 PublicationTreeConfigDescriptor desc = (PublicationTreeConfigDescriptor) contribution; 149 registerTreeConfig(desc); 150 } else if (FACTORY_EP.equals(extensionPoint)) { 151 PublishedDocumentFactoryDescriptor desc = (PublishedDocumentFactoryDescriptor) contribution; 152 factoryDescriptors.put(desc.getName(), desc); 153 } else if (VALIDATORS_RULE_EP.equals(extensionPoint)) { 154 ValidatorsRuleDescriptor desc = (ValidatorsRuleDescriptor) contribution; 155 validatorsRuleDescriptors.put(desc.getName(), desc); 156 } else if (ROOT_SECTION_FINDER_FACTORY_EP.equals(extensionPoint)) { 157 RootSectionFinderFactoryDescriptor desc = (RootSectionFinderFactoryDescriptor) contribution; 158 try { 159 rootSectionFinderFactory = desc.getFactory().newInstance(); 160 } catch (ReflectiveOperationException t) { 161 log.error("Unable to load custom RootSectionFinderFactory", t); 162 } 163 } 164 } 165 166 protected void registerTreeConfig(PublicationTreeConfigDescriptor desc) { 167 if (desc.getParameters().get("RelativeRootPath") != null) { 168 pendingDescriptors.put(desc.getName(), desc); 169 } else { 170 treeConfigDescriptors.put(desc.getName(), desc); 171 } 172 } 173 174 @Override 175 public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 176 if (contribution instanceof PublicationTreeDescriptor) { 177 treeDescriptors.remove(((PublicationTreeDescriptor) contribution).getName()); 178 } else if (contribution instanceof PublicationTreeConfigDescriptor) { 179 String name = ((PublicationTreeConfigDescriptor) contribution).getName(); 180 pendingDescriptors.remove(name); 181 treeConfigDescriptors.remove(name); 182 } else if (contribution instanceof ValidatorsRuleDescriptor) { 183 validatorsRuleDescriptors.remove(((ValidatorsRuleDescriptor) contribution).getName()); 184 } else if (contribution instanceof RootSectionFinderFactoryDescriptor) { 185 rootSectionFinderFactory = null; 186 } 187 } 188 189 @Override 190 public List<String> getAvailablePublicationTree() { 191 List<String> treeConfigs = new ArrayList<>(); 192 treeConfigs.addAll(treeConfigDescriptors.keySet()); 193 return treeConfigs; 194 } 195 196 @Override 197 public Map<String, String> getAvailablePublicationTrees() { 198 Map<String, String> trees = new HashMap<>(); 199 for (PublicationTreeConfigDescriptor desc : treeConfigDescriptors.values()) { 200 String title = desc.getTitle() == null ? desc.getName() : desc.getTitle(); 201 trees.put(desc.getName(), title); 202 } 203 return trees; 204 } 205 206 @Override 207 public PublicationTree getPublicationTree(String treeName, CoreSession coreSession, Map<String, String> params) { 208 return getPublicationTree(treeName, coreSession, params, null); 209 } 210 211 @Override 212 public PublicationTree getPublicationTree(String treeName, CoreSession coreSession, Map<String, String> params, 213 DocumentModel currentDocument) { 214 PublicationTree tree = buildTree(treeName, coreSession, params); 215 if (tree == null) { 216 return null; 217 } 218 if (currentDocument != null) { 219 tree.setCurrentDocument(currentDocument); 220 } 221 return tree; 222 } 223 224 protected PublicationTree buildTree(String treeConfigName, CoreSession coreSession, Map<String, String> params) { 225 PublicationTreeConfigDescriptor config = getPublicationTreeConfigDescriptor(treeConfigName); 226 Map<String, String> allParameters = computeAllParameters(config, params); 227 PublicationTreeDescriptor treeDescriptor = getPublicationTreeDescriptor(config); 228 PublishedDocumentFactory publishedDocumentFactory = getPublishedDocumentFactory(config, treeDescriptor, 229 coreSession, allParameters); 230 return getPublicationTree(treeDescriptor, coreSession, allParameters, publishedDocumentFactory, 231 config.getName(), config.getTitle()); 232 } 233 234 protected Map<String, String> computeAllParameters(PublicationTreeConfigDescriptor config, 235 Map<String, String> params) { 236 final Map<String, String> allParameters = config.getParameters(); 237 if (params != null) { 238 allParameters.putAll(params); 239 } 240 return allParameters; 241 } 242 243 protected PublishedDocumentFactory getPublishedDocumentFactory(PublicationTreeConfigDescriptor config, 244 PublicationTreeDescriptor treeDescriptor, CoreSession coreSession, Map<String, String> params) { 245 PublishedDocumentFactoryDescriptor factoryDesc = getPublishedDocumentFactoryDescriptor(config, treeDescriptor); 246 ValidatorsRule validatorsRule = getValidatorsRule(factoryDesc); 247 248 PublishedDocumentFactory factory; 249 try { 250 factory = factoryDesc.getKlass().newInstance(); 251 } catch (ReflectiveOperationException e) { 252 throw new NuxeoException("Error while creating factory " + factoryDesc.getName(), e); 253 } 254 factory.init(coreSession, validatorsRule, params); 255 return factory; 256 } 257 258 protected ValidatorsRule getValidatorsRule(PublishedDocumentFactoryDescriptor factoryDesc) { 259 String validatorsRuleName = factoryDesc.getValidatorsRuleName(); 260 ValidatorsRule validatorsRule = null; 261 if (validatorsRuleName != null) { 262 ValidatorsRuleDescriptor validatorsRuleDesc = validatorsRuleDescriptors.get(validatorsRuleName); 263 if (validatorsRuleDesc == null) { 264 throw new NuxeoException("Unable to find validatorsRule" + validatorsRuleName); 265 } 266 try { 267 validatorsRule = validatorsRuleDesc.getKlass().newInstance(); 268 } catch (ReflectiveOperationException e) { 269 throw new NuxeoException("Error while creating validatorsRule " + validatorsRuleName, e); 270 } 271 } 272 return validatorsRule; 273 } 274 275 protected PublishedDocumentFactoryDescriptor getPublishedDocumentFactoryDescriptor( 276 PublicationTreeConfigDescriptor config, PublicationTreeDescriptor treeDescriptor) { 277 String factoryName = config.getFactory(); 278 if (factoryName == null) { 279 factoryName = treeDescriptor.getFactory(); 280 } 281 282 PublishedDocumentFactoryDescriptor factoryDesc = factoryDescriptors.get(factoryName); 283 if (factoryDesc == null) { 284 throw new NuxeoException("Unable to find factory" + factoryName); 285 } 286 return factoryDesc; 287 } 288 289 protected PublicationTreeConfigDescriptor getPublicationTreeConfigDescriptor(String treeConfigName) { 290 if (!treeConfigDescriptors.containsKey(treeConfigName)) { 291 throw new NuxeoException("Unknow treeConfig :" + treeConfigName); 292 } 293 return treeConfigDescriptors.get(treeConfigName); 294 } 295 296 protected PublicationTreeDescriptor getPublicationTreeDescriptor(PublicationTreeConfigDescriptor config) { 297 String treeImplName = config.getTree(); 298 if (!treeDescriptors.containsKey(treeImplName)) { 299 throw new NuxeoException("Unknow treeImplementation :" + treeImplName); 300 } 301 return treeDescriptors.get(treeImplName); 302 } 303 304 protected PublicationTree getPublicationTree(PublicationTreeDescriptor treeDescriptor, CoreSession coreSession, 305 Map<String, String> parameters, PublishedDocumentFactory factory, String configName, String treeTitle) { 306 PublicationTree treeImpl; 307 try { 308 treeImpl = treeDescriptor.getKlass().newInstance(); 309 } catch (ReflectiveOperationException e) { 310 throw new NuxeoException("Error while creating tree implementation", e); 311 } 312 treeImpl.initTree(coreSession, parameters, factory, configName, treeTitle); 313 return treeImpl; 314 } 315 316 @Override 317 public PublishedDocument publish(DocumentModel doc, PublicationNode targetNode) { 318 return publish(doc, targetNode, null); 319 } 320 321 @Override 322 public PublishedDocument publish(DocumentModel doc, PublicationNode targetNode, Map<String, String> params) { 323 return targetNode.getTree().publish(doc, targetNode, params); 324 } 325 326 @Override 327 public void unpublish(DocumentModel doc, PublicationNode targetNode) { 328 targetNode.getTree().unpublish(doc, targetNode); 329 } 330 331 @Override 332 public boolean isPublishedDocument(DocumentModel documentModel) { 333 return PublicationRelationHelper.isPublished(documentModel); 334 } 335 336 @Override 337 public PublicationTree getPublicationTreeFor(DocumentModel doc, CoreSession coreSession) { 338 PublicationTree tree = null; 339 try { 340 tree = PublicationRelationHelper.getPublicationTreeUsedForPublishing(doc, coreSession); 341 } catch (NuxeoException e) { 342 // TODO catch proper exception 343 log.error("Unable to get PublicationTree for " + doc.getPathAsString() 344 + ". Fallback on first PublicationTree accepting this document.", e); 345 for (String treeName : treeConfigDescriptors.keySet()) { 346 tree = getPublicationTree(treeName, coreSession, null); 347 if (tree.isPublicationNode(doc)) { 348 break; 349 } 350 } 351 } 352 return tree; 353 } 354 355 @Override 356 public PublicationNode wrapToPublicationNode(DocumentModel documentModel, CoreSession coreSession) { 357 for (String name : getAvailablePublicationTree()) { 358 PublicationTree tree = getPublicationTree(name, coreSession, null); 359 if (tree.isPublicationNode(documentModel)) { 360 return tree.wrapToPublicationNode(documentModel); 361 } 362 } 363 return null; 364 } 365 366 protected void registerPendingDescriptors() { 367 // TODO what to do with multiple repositories? 368 RepositoryManager repositoryManager = Framework.getService(RepositoryManager.class); 369 String repositoryName = repositoryManager.getDefaultRepositoryName(); 370 List<DocumentModel> domains = new DomainsFinder(repositoryName).getDomains(); 371 for (DocumentModel domain : domains) { 372 registerTreeConfigFor(domain); 373 } 374 } 375 376 public void registerTreeConfigFor(DocumentModel domain) { 377 for (PublicationTreeConfigDescriptor desc : pendingDescriptors.values()) { 378 PublicationTreeConfigDescriptor newDesc = new PublicationTreeConfigDescriptor(desc); 379 String newTreeName = desc.getName() + "-" + domain.getName(); 380 newDesc.setName(newTreeName); 381 Path newPath = domain.getPath(); 382 Map<String, String> parameters = newDesc.getParameters(); 383 newPath = newPath.append(parameters.remove(RELATIVE_ROOT_PATH_KEY)); 384 parameters.put(ROOT_PATH_KEY, newPath.toString()); 385 parameters.put(PublisherService.DOMAIN_NAME_KEY, domain.getTitle()); 386 treeConfigDescriptors.put(newDesc.getName(), newDesc); 387 } 388 } 389 390 public void unRegisterTreeConfigFor(DocumentModel domain) { 391 unRegisterTreeConfigFor(domain.getName()); 392 } 393 394 /** 395 * @since 7.3 396 */ 397 public void unRegisterTreeConfigFor(String domainName) { 398 for (PublicationTreeConfigDescriptor desc : pendingDescriptors.values()) { 399 String treeName = desc.getName() + "-" + domainName; 400 treeConfigDescriptors.remove(treeName); 401 } 402 } 403 404 @Override 405 public Map<String, String> getParametersFor(String treeConfigName) { 406 PublicationTreeConfigDescriptor desc = treeConfigDescriptors.get(treeConfigName); 407 Map<String, String> parameters = new HashMap<>(); 408 if (desc != null) { 409 parameters.putAll(desc.getParameters()); 410 } 411 return parameters; 412 } 413 414 @Override 415 public RootSectionFinder getRootSectionFinder(CoreSession session) { 416 if (rootSectionFinderFactory != null) { 417 return rootSectionFinderFactory.getRootSectionFinder(session); 418 } 419 return new DefaultRootSectionsFinder(session); 420 } 421}