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