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 * Florent Guillaume 018 * Laurent Doguin 019 */ 020package org.nuxeo.ecm.core.versioning; 021 022import java.io.Serializable; 023import java.util.ArrayDeque; 024import java.util.Deque; 025import java.util.LinkedHashMap; 026import java.util.List; 027import java.util.Map; 028import java.util.stream.Collectors; 029 030import org.apache.commons.logging.Log; 031import org.apache.commons.logging.LogFactory; 032import org.nuxeo.ecm.core.api.CoreSession; 033import org.nuxeo.ecm.core.api.DocumentModel; 034import org.nuxeo.ecm.core.api.VersioningOption; 035import org.nuxeo.ecm.core.model.Document; 036import org.nuxeo.runtime.api.Framework; 037import org.nuxeo.runtime.logging.DeprecationLogger; 038import org.nuxeo.runtime.model.ComponentContext; 039import org.nuxeo.runtime.model.ComponentInstance; 040import org.nuxeo.runtime.model.DefaultComponent; 041import org.nuxeo.runtime.model.SimpleContributionRegistry; 042 043/** 044 * Versioning service component and implementation. 045 */ 046public class VersioningComponent extends DefaultComponent implements VersioningService { 047 048 private static final Log log = LogFactory.getLog(VersioningComponent.class); 049 050 public static final String VERSIONING_SERVICE_XP = "versioningService"; 051 052 public static final String VERSIONING_RULE_XP = "versioningRules"; 053 054 public static final String VERSIONING_POLICY_XP = "policies"; 055 056 public static final String VERSIONING_FILTER_XP = "filters"; 057 058 public static final String VERSIONING_RESTRICTION_XP = "restrictions"; 059 060 protected static final StandardVersioningService STANDARD_VERSIONING_SERVICE = new StandardVersioningService(); 061 062 protected Map<VersioningServiceDescriptor, VersioningService> versioningServices = new LinkedHashMap<>(); 063 064 /** 065 * @deprecated since 9.1 use 'policy', 'filter' and 'restriction' contributions instead 066 */ 067 @Deprecated 068 protected VersioningRuleRegistry versioningRulesRegistry = new VersioningRuleRegistry(); 069 070 protected VersioningPolicyRegistry versioningPoliciesRegistry = new VersioningPolicyRegistry(); 071 072 protected VersioningFilterRegistry versioningFiltersRegistry = new VersioningFilterRegistry(); 073 074 protected VersioningRestrictionRegistry versioningRestrictionsRegistry = new VersioningRestrictionRegistry(); 075 076 protected static class VersioningPolicyRegistry extends SimpleContributionRegistry<VersioningPolicyDescriptor> { 077 078 @Override 079 public String getContributionId(VersioningPolicyDescriptor contrib) { 080 return contrib.getId(); 081 } 082 083 public Map<String, VersioningPolicyDescriptor> getVersioningPolicyDescriptors() { 084 return currentContribs.entrySet().stream().sorted(Map.Entry.comparingByValue()).collect( 085 Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new)); 086 } 087 088 } 089 090 protected static class VersioningFilterRegistry extends SimpleContributionRegistry<VersioningFilterDescriptor> { 091 092 @Override 093 public String getContributionId(VersioningFilterDescriptor contrib) { 094 return contrib.getId(); 095 } 096 097 public Map<String, VersioningFilterDescriptor> getVersioningFilterDescriptors() { 098 return currentContribs; 099 } 100 101 } 102 103 protected static class VersioningRestrictionRegistry 104 extends SimpleContributionRegistry<VersioningRestrictionDescriptor> { 105 106 @Override 107 public String getContributionId(VersioningRestrictionDescriptor contrib) { 108 return contrib.getType(); 109 } 110 111 public Map<String, VersioningRestrictionDescriptor> getVersioningRestrictionDescriptors() { 112 return currentContribs; 113 } 114 115 } 116 117 /** 118 * @deprecated since 9.1 use 'policy', 'filter' and 'restriction' contributions instead 119 */ 120 @Deprecated 121 protected static class VersioningRuleRegistry extends SimpleContributionRegistry<VersioningRuleDescriptor> { 122 123 @Override 124 public String getContributionId(VersioningRuleDescriptor contrib) { 125 return contrib.getTypeName(); 126 } 127 128 @Override 129 public VersioningRuleDescriptor clone(VersioningRuleDescriptor orig) { 130 return new VersioningRuleDescriptor(orig); 131 } 132 133 @Override 134 public void merge(VersioningRuleDescriptor src, VersioningRuleDescriptor dst) { 135 dst.merge(src); 136 } 137 138 @Override 139 public boolean isSupportingMerge() { 140 return true; 141 } 142 143 @Override 144 public void contributionUpdated(String id, VersioningRuleDescriptor contrib, 145 VersioningRuleDescriptor newOrigContrib) { 146 if (contrib.isEnabled()) { 147 currentContribs.put(id, contrib); 148 } else { 149 currentContribs.remove(id); 150 } 151 } 152 153 public void clear() { 154 currentContribs.clear(); 155 } 156 157 public Map<String, VersioningRuleDescriptor> getVersioningRuleDescriptors() { 158 return currentContribs; 159 } 160 } 161 162 /** 163 * @deprecated since 9.1 use 'policy', 'filter' and 'restriction' contributions instead 164 */ 165 @Deprecated 166 protected Deque<DefaultVersioningRuleDescriptor> defaultVersioningRuleList = new ArrayDeque<>(); 167 168 // public for tests 169 public VersioningService service = null; 170 171 protected ComponentContext context; 172 173 @Override 174 public void activate(ComponentContext context) { 175 this.context = context; 176 this.service = STANDARD_VERSIONING_SERVICE; 177 } 178 179 @Override 180 public void deactivate(ComponentContext context) { 181 this.context = null; 182 this.service = null; 183 } 184 185 @Override 186 public void registerContribution(Object contrib, String point, ComponentInstance contributor) { 187 switch (point) { 188 case VERSIONING_SERVICE_XP: 189 registerVersioningService((VersioningServiceDescriptor) contrib); 190 break; 191 case VERSIONING_RULE_XP: 192 if (contrib instanceof VersioningRuleDescriptor) { 193 VersioningRuleDescriptor rule = (VersioningRuleDescriptor) contrib; 194 registerVersioningRule(rule); 195 String message = String.format( 196 "Versioning rule for '%s' on component %s should now be contributed to extension points '%s', " 197 + "'%s' and '%s': a compatibility registration was performed but it may not be accurate.", 198 (rule).getTypeName(), contributor.getName(), VERSIONING_POLICY_XP, VERSIONING_FILTER_XP, 199 VERSIONING_RESTRICTION_XP); 200 DeprecationLogger.log(message, "9.1"); 201 Framework.getRuntime().getMessageHandler().addWarning(message); 202 } else if (contrib instanceof DefaultVersioningRuleDescriptor) { 203 registerDefaultVersioningRule((DefaultVersioningRuleDescriptor) contrib); 204 String message = String.format("Default versioning rule on component %s should now be contributed to " 205 + "extension points '%s' and '%s': a compatibility registration was performed but it may not be " 206 + "accurate.", contributor.getName(), VERSIONING_POLICY_XP, VERSIONING_RESTRICTION_XP); 207 DeprecationLogger.log(message, "9.1"); 208 Framework.getRuntime().getMessageHandler().addWarning(message); 209 } else { 210 throw new RuntimeException("Unknown contribution to " + point + ": " + contrib.getClass()); 211 } 212 break; 213 case VERSIONING_POLICY_XP: 214 registerVersioningPolicy((VersioningPolicyDescriptor) contrib); 215 break; 216 case VERSIONING_FILTER_XP: 217 registerVersioningFilter((VersioningFilterDescriptor) contrib); 218 break; 219 case VERSIONING_RESTRICTION_XP: 220 registerVersioningRestriction((VersioningRestrictionDescriptor) contrib); 221 break; 222 default: 223 throw new RuntimeException("Unknown extension point: " + point); 224 } 225 } 226 227 @Override 228 public void unregisterContribution(Object contrib, String point, ComponentInstance contributor) { 229 switch (point) { 230 case VERSIONING_SERVICE_XP: 231 unregisterVersioningService((VersioningServiceDescriptor) contrib); 232 break; 233 case VERSIONING_RULE_XP: 234 if (contrib instanceof VersioningRuleDescriptor) { 235 unregisterVersioningRule((VersioningRuleDescriptor) contrib); 236 } else if (contrib instanceof DefaultVersioningRuleDescriptor) { 237 unregisterDefaultVersioningRule((DefaultVersioningRuleDescriptor) contrib); 238 } 239 break; 240 case VERSIONING_POLICY_XP: 241 unregisterVersioningPolicy((VersioningPolicyDescriptor) contrib); 242 break; 243 case VERSIONING_FILTER_XP: 244 unregisterVersioningFilter((VersioningFilterDescriptor) contrib); 245 break; 246 case VERSIONING_RESTRICTION_XP: 247 unregisterVersioningRestriction((VersioningRestrictionDescriptor) contrib); 248 break; 249 default: 250 break; 251 } 252 } 253 254 protected void registerVersioningService(VersioningServiceDescriptor contrib) { 255 String klass = contrib.className; 256 try { 257 VersioningService vs = (VersioningService) context.getRuntimeContext().loadClass(klass).newInstance(); 258 versioningServices.put(contrib, vs); 259 } catch (ReflectiveOperationException e) { 260 throw new RuntimeException("Failed to instantiate: " + klass, e); 261 } 262 log.info("Registered versioning service: " + klass); 263 recompute(); 264 } 265 266 protected void unregisterVersioningService(VersioningServiceDescriptor contrib) { 267 versioningServices.remove(contrib); 268 log.info("Unregistered versioning service: " + contrib.className); 269 recompute(); 270 } 271 272 /** 273 * @deprecated since 9.1, use policy and filter contributions instead 274 */ 275 @Deprecated 276 protected void registerVersioningRule(VersioningRuleDescriptor contrib) { 277 versioningRulesRegistry.addContribution(contrib); 278 log.info("Registered versioning rule: " + contrib.getTypeName()); 279 recompute(); 280 } 281 282 /** 283 * @deprecated since 9.1, use policy and filter contributions instead 284 */ 285 @Deprecated 286 protected void unregisterVersioningRule(VersioningRuleDescriptor contrib) { 287 versioningRulesRegistry.removeContribution(contrib); 288 log.info("Unregistered versioning rule: " + contrib.getTypeName()); 289 recompute(); 290 } 291 292 /** 293 * @deprecated since 9.1, use policy and filter contributions instead 294 */ 295 @Deprecated 296 protected void registerDefaultVersioningRule(DefaultVersioningRuleDescriptor contrib) { 297 // could use a linked set instead, but given the size a linked list is enough 298 defaultVersioningRuleList.add(contrib); 299 recompute(); 300 } 301 302 /** 303 * @deprecated since 9.1, use policy and filter contributions instead 304 */ 305 @Deprecated 306 protected void unregisterDefaultVersioningRule(DefaultVersioningRuleDescriptor contrib) { 307 defaultVersioningRuleList.remove(contrib); 308 recompute(); 309 } 310 311 protected void registerVersioningPolicy(VersioningPolicyDescriptor contrib) { 312 versioningPoliciesRegistry.addContribution(contrib); 313 log.info("Registered versioning policy: " + contrib.getId()); 314 recompute(); 315 } 316 317 protected void unregisterVersioningPolicy(VersioningPolicyDescriptor contrib) { 318 versioningPoliciesRegistry.removeContribution(contrib); 319 log.info("Unregistered versioning policy: " + contrib.getId()); 320 recompute(); 321 } 322 323 protected void registerVersioningFilter(VersioningFilterDescriptor contrib) { 324 versioningFiltersRegistry.addContribution(contrib); 325 log.info("Registered versioning filter: " + contrib.getId()); 326 recompute(); 327 } 328 329 protected void unregisterVersioningFilter(VersioningFilterDescriptor contrib) { 330 versioningFiltersRegistry.removeContribution(contrib); 331 log.info("Unregistered versioning filter: " + contrib.getId()); 332 recompute(); 333 } 334 335 protected void registerVersioningRestriction(VersioningRestrictionDescriptor contrib) { 336 versioningRestrictionsRegistry.addContribution(contrib); 337 log.info("Registered versioning restriction: " + contrib.getType()); 338 recompute(); 339 } 340 341 protected void unregisterVersioningRestriction(VersioningRestrictionDescriptor contrib) { 342 versioningRestrictionsRegistry.removeContribution(contrib); 343 log.info("Unregistered versioning restriction: " + contrib.getType()); 344 recompute(); 345 } 346 347 protected void recompute() { 348 VersioningService versioningService = STANDARD_VERSIONING_SERVICE; 349 for (VersioningService vs : versioningServices.values()) { 350 versioningService = vs; 351 } 352 if (versioningService instanceof ExtendableVersioningService) { 353 ExtendableVersioningService evs = (ExtendableVersioningService) versioningService; 354 evs.setVersioningPolicies(getVersioningPolicies()); 355 evs.setVersioningFilters(getVersioningFilters()); 356 evs.setVersioningRestrictions(getVersioningRestrictions()); 357 evs.setVersioningRules(getVersioningRules()); 358 evs.setDefaultVersioningRule(getDefaultVersioningRule()); 359 } 360 this.service = versioningService; 361 } 362 363 /** 364 * @deprecated since 9.1, use policy and filter contributions instead 365 */ 366 @Deprecated 367 protected Map<String, VersioningRuleDescriptor> getVersioningRules() { 368 return versioningRulesRegistry.getVersioningRuleDescriptors(); 369 } 370 371 /** 372 * @deprecated since 9.1, use policy and filter contributions instead 373 */ 374 @Deprecated 375 protected DefaultVersioningRuleDescriptor getDefaultVersioningRule() { 376 return defaultVersioningRuleList.peekLast(); 377 } 378 379 protected Map<String, VersioningPolicyDescriptor> getVersioningPolicies() { 380 return versioningPoliciesRegistry.getVersioningPolicyDescriptors(); 381 } 382 383 protected Map<String, VersioningFilterDescriptor> getVersioningFilters() { 384 return versioningFiltersRegistry.getVersioningFilterDescriptors(); 385 } 386 387 protected Map<String, VersioningRestrictionDescriptor> getVersioningRestrictions() { 388 return versioningRestrictionsRegistry.getVersioningRestrictionDescriptors(); 389 } 390 391 @Override 392 public String getVersionLabel(DocumentModel doc) { 393 return service.getVersionLabel(doc); 394 } 395 396 @Override 397 public void doPostCreate(Document doc, Map<String, Serializable> options) { 398 service.doPostCreate(doc, options); 399 } 400 401 @Override 402 public List<VersioningOption> getSaveOptions(DocumentModel docModel) { 403 return service.getSaveOptions(docModel); 404 } 405 406 @Override 407 public boolean isPreSaveDoingCheckOut(Document doc, boolean isDirty, VersioningOption option, 408 Map<String, Serializable> options) { 409 return service.isPreSaveDoingCheckOut(doc, isDirty, option, options); 410 } 411 412 @Override 413 public VersioningOption doPreSave(CoreSession session, Document doc, boolean isDirty, VersioningOption option, 414 String checkinComment, Map<String, Serializable> options) { 415 return service.doPreSave(session, doc, isDirty, option, checkinComment, options); 416 } 417 418 @Override 419 public boolean isPostSaveDoingCheckIn(Document doc, VersioningOption option, Map<String, Serializable> options) { 420 return service.isPostSaveDoingCheckIn(doc, option, options); 421 } 422 423 @Override 424 public Document doPostSave(CoreSession session, Document doc, VersioningOption option, String checkinComment, 425 Map<String, Serializable> options) { 426 return service.doPostSave(session, doc, option, checkinComment, options); 427 } 428 429 @Override 430 public Document doCheckIn(Document doc, VersioningOption option, String checkinComment) { 431 return service.doCheckIn(doc, option, checkinComment); 432 } 433 434 @Override 435 public void doCheckOut(Document doc) { 436 service.doCheckOut(doc); 437 } 438 439 @Override 440 public void doAutomaticVersioning(DocumentModel previousDocument, DocumentModel currentDocument, boolean before) { 441 service.doAutomaticVersioning(previousDocument, currentDocument, before); 442 } 443 444}