/* * (C) Copyright 2006-2017 Nuxeo (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: * Florent Guillaume * Laurent Doguin */ package org.nuxeo.ecm.core.versioning; import java.io.Serializable; import java.util.ArrayDeque; import java.util.Deque; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.nuxeo.ecm.core.api.DocumentModel; import org.nuxeo.ecm.core.api.VersioningOption; import org.nuxeo.ecm.core.model.Document; import org.nuxeo.runtime.api.Framework; import org.nuxeo.runtime.logging.DeprecationLogger; import org.nuxeo.runtime.model.ComponentContext; import org.nuxeo.runtime.model.ComponentInstance; import org.nuxeo.runtime.model.DefaultComponent; import org.nuxeo.runtime.model.SimpleContributionRegistry; /** * Versioning service component and implementation. */ public class VersioningComponent extends DefaultComponent implements VersioningService { private static final Log log = LogFactory.getLog(VersioningComponent.class); public static final String VERSIONING_SERVICE_XP = "versioningService"; public static final String VERSIONING_RULE_XP = "versioningRules"; public static final String VERSIONING_POLICY_XP = "policies"; public static final String VERSIONING_FILTER_XP = "filters"; public static final String VERSIONING_RESTRICTION_XP = "restrictions"; protected static final StandardVersioningService STANDARD_VERSIONING_SERVICE = new StandardVersioningService(); protected Map<VersioningServiceDescriptor, VersioningService> versioningServices = new LinkedHashMap<>(); /** * @deprecated since 9.1 use 'policy', 'filter' and 'restriction' contributions instead */ @Deprecated protected VersioningRuleRegistry versioningRulesRegistry = new VersioningRuleRegistry(); protected VersioningPolicyRegistry versioningPoliciesRegistry = new VersioningPolicyRegistry(); protected VersioningFilterRegistry versioningFiltersRegistry = new VersioningFilterRegistry(); protected VersioningRestrictionRegistry versioningRestrictionsRegistry = new VersioningRestrictionRegistry(); protected static class VersioningPolicyRegistry extends SimpleContributionRegistry<VersioningPolicyDescriptor> { @Override public String getContributionId(VersioningPolicyDescriptor contrib) { return contrib.getId(); } public Map<String, VersioningPolicyDescriptor> getVersioningPolicyDescriptors() { return currentContribs.entrySet().stream().sorted(Map.Entry.comparingByValue()).collect( Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new)); } } protected static class VersioningFilterRegistry extends SimpleContributionRegistry<VersioningFilterDescriptor> { @Override public String getContributionId(VersioningFilterDescriptor contrib) { return contrib.getId(); } public Map<String, VersioningFilterDescriptor> getVersioningFilterDescriptors() { return currentContribs; } } protected static class VersioningRestrictionRegistry extends SimpleContributionRegistry<VersioningRestrictionDescriptor> { @Override public String getContributionId(VersioningRestrictionDescriptor contrib) { return contrib.getType(); } public Map<String, VersioningRestrictionDescriptor> getVersioningRestrictionDescriptors() { return currentContribs; } } /** * @deprecated since 9.1 use 'policy', 'filter' and 'restriction' contributions instead */ @Deprecated protected static class VersioningRuleRegistry extends SimpleContributionRegistry<VersioningRuleDescriptor> { @Override public String getContributionId(VersioningRuleDescriptor contrib) { return contrib.getTypeName(); } @Override public VersioningRuleDescriptor clone(VersioningRuleDescriptor orig) { return new VersioningRuleDescriptor(orig); } @Override public void merge(VersioningRuleDescriptor src, VersioningRuleDescriptor dst) { dst.merge(src); } @Override public boolean isSupportingMerge() { return true; } @Override public void contributionUpdated(String id, VersioningRuleDescriptor contrib, VersioningRuleDescriptor newOrigContrib) { if (contrib.isEnabled()) { currentContribs.put(id, contrib); } else { currentContribs.remove(id); } } public void clear() { currentContribs.clear(); } public Map<String, VersioningRuleDescriptor> getVersioningRuleDescriptors() { return currentContribs; } } /** * @deprecated since 9.1 use 'policy', 'filter' and 'restriction' contributions instead */ @Deprecated protected Deque<DefaultVersioningRuleDescriptor> defaultVersioningRuleList = new ArrayDeque<>(); // public for tests public VersioningService service = STANDARD_VERSIONING_SERVICE; protected ComponentContext context; @Override public void activate(ComponentContext context) { this.context = context; } @Override public void deactivate(ComponentContext context) { this.context = null; } @Override public void registerContribution(Object contrib, String point, ComponentInstance contributor) { switch (point) { case VERSIONING_SERVICE_XP: registerVersioningService((VersioningServiceDescriptor) contrib); break; case VERSIONING_RULE_XP: if (contrib instanceof VersioningRuleDescriptor) { VersioningRuleDescriptor rule = (VersioningRuleDescriptor) contrib; registerVersioningRule(rule); String message = String.format( "Versioning rule for '%s' on component %s should now be contributed to extension points '%s', " + "'%s' and '%s': a compatibility registration was performed but it may not be accurate.", (rule).getTypeName(), contributor.getName(), VERSIONING_POLICY_XP, VERSIONING_FILTER_XP, VERSIONING_RESTRICTION_XP); DeprecationLogger.log(message, "9.1"); Framework.getRuntime().getWarnings().add(message); } else if (contrib instanceof DefaultVersioningRuleDescriptor) { registerDefaultVersioningRule((DefaultVersioningRuleDescriptor) contrib); String message = String.format("Default versioning rule on component %s should now be contributed to " + "extension points '%s' and '%s': a compatibility registration was performed but it may not be " + "accurate.", contributor.getName(), VERSIONING_POLICY_XP, VERSIONING_RESTRICTION_XP); DeprecationLogger.log(message, "9.1"); Framework.getRuntime().getWarnings().add(message); } else { throw new RuntimeException("Unknown contribution to " + point + ": " + contrib.getClass()); } break; case VERSIONING_POLICY_XP: registerVersioningPolicy((VersioningPolicyDescriptor) contrib); break; case VERSIONING_FILTER_XP: registerVersioningFilter((VersioningFilterDescriptor) contrib); break; case VERSIONING_RESTRICTION_XP: registerVersioningRestriction((VersioningRestrictionDescriptor) contrib); break; default: throw new RuntimeException("Unknown extension point: " + point); } } @Override public void unregisterContribution(Object contrib, String point, ComponentInstance contributor) { switch (point) { case VERSIONING_SERVICE_XP: unregisterVersioningService((VersioningServiceDescriptor) contrib); break; case VERSIONING_RULE_XP: if (contrib instanceof VersioningRuleDescriptor) { unregisterVersioningRule((VersioningRuleDescriptor) contrib); } else if (contrib instanceof DefaultVersioningRuleDescriptor) { unregisterDefaultVersioningRule((DefaultVersioningRuleDescriptor) contrib); } break; case VERSIONING_POLICY_XP: unregisterVersioningPolicy((VersioningPolicyDescriptor) contrib); break; case VERSIONING_FILTER_XP: unregisterVersioningFilter((VersioningFilterDescriptor) contrib); break; case VERSIONING_RESTRICTION_XP: unregisterVersioningRestriction((VersioningRestrictionDescriptor) contrib); break; default: break; } } protected void registerVersioningService(VersioningServiceDescriptor contrib) { String klass = contrib.className; try { VersioningService vs = (VersioningService) context.getRuntimeContext().loadClass(klass).newInstance(); versioningServices.put(contrib, vs); } catch (ReflectiveOperationException e) { throw new RuntimeException("Failed to instantiate: " + klass, e); } log.info("Registered versioning service: " + klass); recompute(); } protected void unregisterVersioningService(VersioningServiceDescriptor contrib) { versioningServices.remove(contrib); log.info("Unregistered versioning service: " + contrib.className); recompute(); } /** * @deprecated since 9.1, use policy and filter contributions instead */ @Deprecated protected void registerVersioningRule(VersioningRuleDescriptor contrib) { versioningRulesRegistry.addContribution(contrib); log.info("Registered versioning rule: " + contrib.getTypeName()); recompute(); } /** * @deprecated since 9.1, use policy and filter contributions instead */ @Deprecated protected void unregisterVersioningRule(VersioningRuleDescriptor contrib) { versioningRulesRegistry.removeContribution(contrib); log.info("Unregistered versioning rule: " + contrib.getTypeName()); recompute(); } /** * @deprecated since 9.1, use policy and filter contributions instead */ @Deprecated protected void registerDefaultVersioningRule(DefaultVersioningRuleDescriptor contrib) { // could use a linked set instead, but given the size a linked list is enough defaultVersioningRuleList.add(contrib); recompute(); } /** * @deprecated since 9.1, use policy and filter contributions instead */ @Deprecated protected void unregisterDefaultVersioningRule(DefaultVersioningRuleDescriptor contrib) { defaultVersioningRuleList.remove(contrib); recompute(); } protected void registerVersioningPolicy(VersioningPolicyDescriptor contrib) { versioningPoliciesRegistry.addContribution(contrib); log.info("Registered versioning policy: " + contrib.getId()); recompute(); } protected void unregisterVersioningPolicy(VersioningPolicyDescriptor contrib) { versioningPoliciesRegistry.removeContribution(contrib); log.info("Unregistered versioning policy: " + contrib.getId()); recompute(); } protected void registerVersioningFilter(VersioningFilterDescriptor contrib) { versioningFiltersRegistry.addContribution(contrib); log.info("Registered versioning filter: " + contrib.getId()); recompute(); } protected void unregisterVersioningFilter(VersioningFilterDescriptor contrib) { versioningFiltersRegistry.removeContribution(contrib); log.info("Unregistered versioning filter: " + contrib.getId()); recompute(); } protected void registerVersioningRestriction(VersioningRestrictionDescriptor contrib) { versioningRestrictionsRegistry.addContribution(contrib); log.info("Registered versioning restriction: " + contrib.getType()); recompute(); } protected void unregisterVersioningRestriction(VersioningRestrictionDescriptor contrib) { versioningRestrictionsRegistry.removeContribution(contrib); log.info("Unregistered versioning restriction: " + contrib.getType()); recompute(); } protected void recompute() { VersioningService versioningService = STANDARD_VERSIONING_SERVICE; for (VersioningService vs : versioningServices.values()) { versioningService = vs; } if (versioningService instanceof ExtendableVersioningService) { ExtendableVersioningService evs = (ExtendableVersioningService) versioningService; evs.setVersioningPolicies(getVersioningPolicies()); evs.setVersioningFilters(getVersioningFilters()); evs.setVersioningRestrictions(getVersioningRestrictions()); evs.setVersioningRules(getVersioningRules()); evs.setDefaultVersioningRule(getDefaultVersioningRule()); } this.service = versioningService; } /** * @deprecated since 9.1, use policy and filter contributions instead */ @Deprecated protected Map<String, VersioningRuleDescriptor> getVersioningRules() { return versioningRulesRegistry.getVersioningRuleDescriptors(); } /** * @deprecated since 9.1, use policy and filter contributions instead */ @Deprecated protected DefaultVersioningRuleDescriptor getDefaultVersioningRule() { return defaultVersioningRuleList.peekLast(); } protected Map<String, VersioningPolicyDescriptor> getVersioningPolicies() { return versioningPoliciesRegistry.getVersioningPolicyDescriptors(); } protected Map<String, VersioningFilterDescriptor> getVersioningFilters() { return versioningFiltersRegistry.getVersioningFilterDescriptors(); } protected Map<String, VersioningRestrictionDescriptor> getVersioningRestrictions() { return versioningRestrictionsRegistry.getVersioningRestrictionDescriptors(); } @Override public String getVersionLabel(DocumentModel doc) { return service.getVersionLabel(doc); } @Override public void doPostCreate(Document doc, Map<String, Serializable> options) { service.doPostCreate(doc, options); } @Override public List<VersioningOption> getSaveOptions(DocumentModel docModel) { return service.getSaveOptions(docModel); } @Override public boolean isPreSaveDoingCheckOut(Document doc, boolean isDirty, VersioningOption option, Map<String, Serializable> options) { return service.isPreSaveDoingCheckOut(doc, isDirty, option, options); } @Override public VersioningOption doPreSave(Document doc, boolean isDirty, VersioningOption option, String checkinComment, Map<String, Serializable> options) { return service.doPreSave(doc, isDirty, option, checkinComment, options); } @Override public boolean isPostSaveDoingCheckIn(Document doc, VersioningOption option, Map<String, Serializable> options) { return service.isPostSaveDoingCheckIn(doc, option, options); } @Override public Document doPostSave(Document doc, VersioningOption option, String checkinComment, Map<String, Serializable> options) { return service.doPostSave(doc, option, checkinComment, options); } @Override public Document doCheckIn(Document doc, VersioningOption option, String checkinComment) { return service.doCheckIn(doc, option, checkinComment); } @Override public void doCheckOut(Document doc) { service.doCheckOut(doc); } @Override public void doAutomaticVersioning(DocumentModel previousDocument, DocumentModel currentDocument, boolean before) { service.doAutomaticVersioning(previousDocument, currentDocument, before); } }