/* * The MIT License * * Copyright (c) 2012, Dominik Bartholdi, Seiji Sogabe * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package jenkins.model; import hudson.DescriptorExtensionList; import hudson.Extension; import hudson.ExtensionPoint; import hudson.Util; import hudson.model.Describable; import hudson.model.Descriptor; import hudson.model.Failure; import jenkins.model.Messages; import hudson.util.FormValidation; import java.io.IOException; import java.io.Serializable; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; import javax.servlet.ServletException; import org.apache.commons.lang.StringUtils; import org.jenkinsci.Symbol; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.QueryParameter; /** * This ExtensionPoint allows to enforce the name of projects/jobs. * * @author Dominik Bartholdi (imod) */ public abstract class ProjectNamingStrategy implements Describable<ProjectNamingStrategy>, ExtensionPoint { public ProjectNamingStrategyDescriptor getDescriptor() { return (ProjectNamingStrategyDescriptor) Jenkins.getInstance().getDescriptor(getClass()); } public static DescriptorExtensionList<ProjectNamingStrategy, ProjectNamingStrategyDescriptor> all() { return Jenkins.getInstance().<ProjectNamingStrategy, ProjectNamingStrategyDescriptor> getDescriptorList(ProjectNamingStrategy.class); } /** * Called when creating a new job. * * @param name * the name given from the UI * @throws Failure * if the user has to be informed about an illegal name, forces the user to change the name before submitting. The message of the failure will be presented to the user. */ public void checkName(String name) throws Failure { // no op } /** * This flag can be used to force existing jobs to be migrated to a new naming strategy - if this method returns true, the naming will be enforced at every config change. If <code>false</code> is * returned, only new jobs have to follow the strategy. * * @return <code>true</code> if existing jobs should be enforced to confirm to the naming standard. */ public boolean isForceExistingJobs() { return false; } /** * The default naming strategy which does not restrict the name of a job. */ public static final ProjectNamingStrategy DEFAULT_NAMING_STRATEGY = new DefaultProjectNamingStrategy(); /** * Default implementation which does not restrict the name to any form. */ public static final class DefaultProjectNamingStrategy extends ProjectNamingStrategy implements Serializable { private static final long serialVersionUID = 1L; @DataBoundConstructor public DefaultProjectNamingStrategy() { } @Override public void checkName(String origName) throws Failure { // default - should just do nothing (this is how Jenkins worked before introducing this ExtensionPoint) } /** * DefaultProjectNamingStrategy is stateless, therefore save to keep the same instance */ private Object readResolve() { return DEFAULT_NAMING_STRATEGY; } @Extension @Symbol("standard") public static final class DescriptorImpl extends ProjectNamingStrategyDescriptor { @Override public String getDisplayName() { return Messages.DefaultProjectNamingStrategy_DisplayName(); } @Override public String getHelpFile() { return "/help/system-config/defaultJobNamingStrategy.html"; } } } /** * Naming strategy which allows the admin to define a pattern a job's name has to follow. */ public static final class PatternProjectNamingStrategy extends ProjectNamingStrategy implements Serializable { private static final long serialVersionUID = 1L; /** * regex pattern a job's name has to follow */ private final String namePattern; private final String description; private boolean forceExistingJobs; @Deprecated public PatternProjectNamingStrategy(String namePattern, boolean forceExistingJobs) { this(namePattern, null, forceExistingJobs); } /** @since 1.533 */ @DataBoundConstructor public PatternProjectNamingStrategy(String namePattern, String description, boolean forceExistingJobs) { this.namePattern = namePattern; this.description = description; this.forceExistingJobs = forceExistingJobs; } @Override public void checkName(String name) { if (StringUtils.isNotBlank(namePattern) && StringUtils.isNotBlank(name)) { if (!Pattern.matches(namePattern, name)) { throw new Failure(StringUtils.isEmpty(description) ? Messages.Hudson_JobNameConventionNotApplyed(name, namePattern) : description); } } } public String getNamePattern() { return namePattern; } /** @since 1.533 */ public String getDescription() { return description; } public boolean isForceExistingJobs() { return forceExistingJobs; } @Extension @Symbol("pattern") public static final class DescriptorImpl extends ProjectNamingStrategyDescriptor { public static final String DEFAULT_PATTERN = ".*"; @Override public String getDisplayName() { return Messages.PatternProjectNamingStrategy_DisplayName(); } @Override public String getHelpFile() { return "/help/system-config/patternJobNamingStrategy.html"; } public FormValidation doCheckNamePattern(@QueryParameter String value) throws IOException, ServletException { String pattern = Util.fixEmptyAndTrim(value); if (pattern == null) { return FormValidation.error(Messages.PatternProjectNamingStrategy_NamePatternRequired()); } try { Pattern.compile(pattern); } catch (PatternSyntaxException e) { return FormValidation.error(Messages.PatternProjectNamingStrategy_NamePatternInvalidSyntax()); } return FormValidation.ok(); } } } public static abstract class ProjectNamingStrategyDescriptor extends Descriptor<ProjectNamingStrategy> { } }