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 = STANDARD_VERSIONING_SERVICE; 169 170 protected ComponentContext context; 171 172 @Override 173 public void activate(ComponentContext context) { 174 this.context = context; 175 } 176 177 @Override 178 public void deactivate(ComponentContext context) { 179 this.context = null; 180 } 181 182 @Override 183 public void registerContribution(Object contrib, String point, ComponentInstance contributor) { 184 switch (point) { 185 case VERSIONING_SERVICE_XP: 186 registerVersioningService((VersioningServiceDescriptor) contrib); 187 break; 188 case VERSIONING_RULE_XP: 189 if (contrib instanceof VersioningRuleDescriptor) { 190 VersioningRuleDescriptor rule = (VersioningRuleDescriptor) contrib; 191 registerVersioningRule(rule); 192 String message = String.format( 193 "Versioning rule for '%s' on component %s should now be contributed to extension points '%s', " 194 + "'%s' and '%s': a compatibility registration was performed but it may not be accurate.", 195 (rule).getTypeName(), contributor.getName(), VERSIONING_POLICY_XP, VERSIONING_FILTER_XP, 196 VERSIONING_RESTRICTION_XP); 197 DeprecationLogger.log(message, "9.1"); 198 Framework.getRuntime().getWarnings().add(message); 199 } else if (contrib instanceof DefaultVersioningRuleDescriptor) { 200 registerDefaultVersioningRule((DefaultVersioningRuleDescriptor) contrib); 201 String message = String.format("Default versioning rule on component %s should now be contributed to " 202 + "extension points '%s' and '%s': a compatibility registration was performed but it may not be " 203 + "accurate.", contributor.getName(), VERSIONING_POLICY_XP, VERSIONING_RESTRICTION_XP); 204 DeprecationLogger.log(message, "9.1"); 205 Framework.getRuntime().getWarnings().add(message); 206 } else { 207 throw new RuntimeException("Unknown contribution to " + point + ": " + contrib.getClass()); 208 } 209 break; 210 case VERSIONING_POLICY_XP: 211 registerVersioningPolicy((VersioningPolicyDescriptor) contrib); 212 break; 213 case VERSIONING_FILTER_XP: 214 registerVersioningFilter((VersioningFilterDescriptor) contrib); 215 break; 216 case VERSIONING_RESTRICTION_XP: 217 registerVersioningRestriction((VersioningRestrictionDescriptor) contrib); 218 break; 219 default: 220 throw new RuntimeException("Unknown extension point: " + point); 221 } 222 } 223 224 @Override 225 public void unregisterContribution(Object contrib, String point, ComponentInstance contributor) { 226 switch (point) { 227 case VERSIONING_SERVICE_XP: 228 unregisterVersioningService((VersioningServiceDescriptor) contrib); 229 break; 230 case VERSIONING_RULE_XP: 231 if (contrib instanceof VersioningRuleDescriptor) { 232 unregisterVersioningRule((VersioningRuleDescriptor) contrib); 233 } else if (contrib instanceof DefaultVersioningRuleDescriptor) { 234 unregisterDefaultVersioningRule((DefaultVersioningRuleDescriptor) contrib); 235 } 236 break; 237 case VERSIONING_POLICY_XP: 238 unregisterVersioningPolicy((VersioningPolicyDescriptor) contrib); 239 break; 240 case VERSIONING_FILTER_XP: 241 unregisterVersioningFilter((VersioningFilterDescriptor) contrib); 242 break; 243 case VERSIONING_RESTRICTION_XP: 244 unregisterVersioningRestriction((VersioningRestrictionDescriptor) contrib); 245 break; 246 default: 247 break; 248 } 249 } 250 251 protected void registerVersioningService(VersioningServiceDescriptor contrib) { 252 String klass = contrib.className; 253 try { 254 VersioningService vs = (VersioningService) context.getRuntimeContext().loadClass(klass).newInstance(); 255 versioningServices.put(contrib, vs); 256 } catch (ReflectiveOperationException e) { 257 throw new RuntimeException("Failed to instantiate: " + klass, e); 258 } 259 log.info("Registered versioning service: " + klass); 260 recompute(); 261 } 262 263 protected void unregisterVersioningService(VersioningServiceDescriptor contrib) { 264 versioningServices.remove(contrib); 265 log.info("Unregistered versioning service: " + contrib.className); 266 recompute(); 267 } 268 269 /** 270 * @deprecated since 9.1, use policy and filter contributions instead 271 */ 272 @Deprecated 273 protected void registerVersioningRule(VersioningRuleDescriptor contrib) { 274 versioningRulesRegistry.addContribution(contrib); 275 log.info("Registered versioning rule: " + contrib.getTypeName()); 276 recompute(); 277 } 278 279 /** 280 * @deprecated since 9.1, use policy and filter contributions instead 281 */ 282 @Deprecated 283 protected void unregisterVersioningRule(VersioningRuleDescriptor contrib) { 284 versioningRulesRegistry.removeContribution(contrib); 285 log.info("Unregistered versioning rule: " + contrib.getTypeName()); 286 recompute(); 287 } 288 289 /** 290 * @deprecated since 9.1, use policy and filter contributions instead 291 */ 292 @Deprecated 293 protected void registerDefaultVersioningRule(DefaultVersioningRuleDescriptor contrib) { 294 // could use a linked set instead, but given the size a linked list is enough 295 defaultVersioningRuleList.add(contrib); 296 recompute(); 297 } 298 299 /** 300 * @deprecated since 9.1, use policy and filter contributions instead 301 */ 302 @Deprecated 303 protected void unregisterDefaultVersioningRule(DefaultVersioningRuleDescriptor contrib) { 304 defaultVersioningRuleList.remove(contrib); 305 recompute(); 306 } 307 308 protected void registerVersioningPolicy(VersioningPolicyDescriptor contrib) { 309 versioningPoliciesRegistry.addContribution(contrib); 310 log.info("Registered versioning policy: " + contrib.getId()); 311 recompute(); 312 } 313 314 protected void unregisterVersioningPolicy(VersioningPolicyDescriptor contrib) { 315 versioningPoliciesRegistry.removeContribution(contrib); 316 log.info("Unregistered versioning policy: " + contrib.getId()); 317 recompute(); 318 } 319 320 protected void registerVersioningFilter(VersioningFilterDescriptor contrib) { 321 versioningFiltersRegistry.addContribution(contrib); 322 log.info("Registered versioning filter: " + contrib.getId()); 323 recompute(); 324 } 325 326 protected void unregisterVersioningFilter(VersioningFilterDescriptor contrib) { 327 versioningFiltersRegistry.removeContribution(contrib); 328 log.info("Unregistered versioning filter: " + contrib.getId()); 329 recompute(); 330 } 331 332 protected void registerVersioningRestriction(VersioningRestrictionDescriptor contrib) { 333 versioningRestrictionsRegistry.addContribution(contrib); 334 log.info("Registered versioning restriction: " + contrib.getType()); 335 recompute(); 336 } 337 338 protected void unregisterVersioningRestriction(VersioningRestrictionDescriptor contrib) { 339 versioningRestrictionsRegistry.removeContribution(contrib); 340 log.info("Unregistered versioning restriction: " + contrib.getType()); 341 recompute(); 342 } 343 344 protected void recompute() { 345 VersioningService versioningService = STANDARD_VERSIONING_SERVICE; 346 for (VersioningService vs : versioningServices.values()) { 347 versioningService = vs; 348 } 349 if (versioningService instanceof ExtendableVersioningService) { 350 ExtendableVersioningService evs = (ExtendableVersioningService) versioningService; 351 evs.setVersioningPolicies(getVersioningPolicies()); 352 evs.setVersioningFilters(getVersioningFilters()); 353 evs.setVersioningRestrictions(getVersioningRestrictions()); 354 evs.setVersioningRules(getVersioningRules()); 355 evs.setDefaultVersioningRule(getDefaultVersioningRule()); 356 } 357 this.service = versioningService; 358 } 359 360 /** 361 * @deprecated since 9.1, use policy and filter contributions instead 362 */ 363 @Deprecated 364 protected Map<String, VersioningRuleDescriptor> getVersioningRules() { 365 return versioningRulesRegistry.getVersioningRuleDescriptors(); 366 } 367 368 /** 369 * @deprecated since 9.1, use policy and filter contributions instead 370 */ 371 @Deprecated 372 protected DefaultVersioningRuleDescriptor getDefaultVersioningRule() { 373 return defaultVersioningRuleList.peekLast(); 374 } 375 376 protected Map<String, VersioningPolicyDescriptor> getVersioningPolicies() { 377 return versioningPoliciesRegistry.getVersioningPolicyDescriptors(); 378 } 379 380 protected Map<String, VersioningFilterDescriptor> getVersioningFilters() { 381 return versioningFiltersRegistry.getVersioningFilterDescriptors(); 382 } 383 384 protected Map<String, VersioningRestrictionDescriptor> getVersioningRestrictions() { 385 return versioningRestrictionsRegistry.getVersioningRestrictionDescriptors(); 386 } 387 388 @Override 389 public String getVersionLabel(DocumentModel doc) { 390 return service.getVersionLabel(doc); 391 } 392 393 @Override 394 public void doPostCreate(Document doc, Map<String, Serializable> options) { 395 service.doPostCreate(doc, options); 396 } 397 398 @Override 399 public List<VersioningOption> getSaveOptions(DocumentModel docModel) { 400 return service.getSaveOptions(docModel); 401 } 402 403 @Override 404 public boolean isPreSaveDoingCheckOut(Document doc, boolean isDirty, VersioningOption option, 405 Map<String, Serializable> options) { 406 return service.isPreSaveDoingCheckOut(doc, isDirty, option, options); 407 } 408 409 @Override 410 public VersioningOption doPreSave(Document doc, boolean isDirty, VersioningOption option, String checkinComment, 411 Map<String, Serializable> options) { 412 return service.doPreSave(doc, isDirty, option, checkinComment, options); 413 } 414 415 @Override 416 public boolean isPostSaveDoingCheckIn(Document doc, VersioningOption option, Map<String, Serializable> options) { 417 return service.isPostSaveDoingCheckIn(doc, option, options); 418 } 419 420 @Override 421 public Document doPostSave(Document doc, VersioningOption option, String checkinComment, 422 Map<String, Serializable> options) { 423 return service.doPostSave(doc, option, checkinComment, options); 424 } 425 426 @Override 427 public Document doCheckIn(Document doc, VersioningOption option, String checkinComment) { 428 return service.doCheckIn(doc, option, checkinComment); 429 } 430 431 @Override 432 public void doCheckOut(Document doc) { 433 service.doCheckOut(doc); 434 } 435 436 @Override 437 public void doAutomaticVersioning(DocumentModel previousDocument, DocumentModel currentDocument, boolean before) { 438 service.doAutomaticVersioning(previousDocument, currentDocument, before); 439 } 440 441}