001/** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.camel.impl; 018 019import java.util.concurrent.atomic.AtomicLong; 020import java.util.regex.Matcher; 021import java.util.regex.Pattern; 022 023import org.apache.camel.CamelContext; 024import org.apache.camel.spi.ManagementNameStrategy; 025import org.apache.camel.util.ObjectHelper; 026 027/** 028 * Default implementation of {@link ManagementNameStrategy} 029 * <p/> 030 * This implementation will by default use a name pattern as <tt>#name#</tt> and in case 031 * of a clash, then the pattern will fallback to be using the counter as <tt>#name#-#counter#</tt>. 032 */ 033public class DefaultManagementNameStrategy implements ManagementNameStrategy { 034 035 private static final Pattern INVALID_PATTERN = Pattern.compile(".*#\\w+#.*"); 036 private static final AtomicLong NAME_COUNTER = new AtomicLong(); 037 038 private final CamelContext camelContext; 039 private final String defaultPattern; 040 private final String nextPattern; 041 private String name; 042 private String namePattern; 043 044 public DefaultManagementNameStrategy(CamelContext camelContext) { 045 this(camelContext, null, "#name#-#counter#"); 046 } 047 048 public DefaultManagementNameStrategy(CamelContext camelContext, String defaultPattern, String nextPattern) { 049 this.camelContext = camelContext; 050 this.defaultPattern = defaultPattern; 051 this.nextPattern = nextPattern; 052 } 053 054 @Override 055 public String getNamePattern() { 056 return namePattern; 057 } 058 059 @Override 060 public void setNamePattern(String namePattern) { 061 this.namePattern = namePattern; 062 } 063 064 @Override 065 public String getName() { 066 if (name == null) { 067 String pattern = getNamePattern(); 068 if (pattern == null) { 069 // fallback and use the default pattern which is the same name as the CamelContext has been given 070 pattern = defaultPattern != null ? defaultPattern : camelContext.getManagementStrategy().getManagementAgent().getManagementNamePattern(); 071 } 072 name = resolveManagementName(pattern, camelContext.getName(), true); 073 } 074 return name; 075 } 076 077 @Override 078 public String getNextName() { 079 if (isFixedName()) { 080 // use the fixed name 081 return getName(); 082 } else { 083 // or resolve a new name 084 String pattern = getNamePattern(); 085 if (pattern == null) { 086 // use a pattern that has a counter to ensure unique next name 087 pattern = nextPattern; 088 } 089 return resolveManagementName(pattern, camelContext.getName(), true); 090 } 091 } 092 093 @Override 094 public boolean isFixedName() { 095 // the name will be fixed unless there is a counter token 096 String pattern = getNamePattern(); 097 if (pattern == null) { 098 // we are not fixed by default 099 return false; 100 } 101 return !pattern.contains("#counter#"); 102 } 103 104 /** 105 * Creates a new management name with the given pattern 106 * 107 * @param pattern the pattern 108 * @param name the name 109 * @return the management name 110 * @throws IllegalArgumentException if the pattern or name is invalid or empty 111 */ 112 public String resolveManagementName(String pattern, String name, boolean invalidCheck) { 113 ObjectHelper.notEmpty(pattern, "pattern"); 114 ObjectHelper.notEmpty(name, "name"); 115 116 // must quote the names to have it work as literal replacement 117 name = Matcher.quoteReplacement(name); 118 119 // replace tokens 120 String answer = pattern; 121 if (pattern.contains("#counter#")) { 122 // only increment the counter on-demand 123 answer = pattern.replaceFirst("#counter#", "" + nextNameCounter()); 124 } 125 // camelId and name is the same tokens 126 answer = answer.replaceFirst("#camelId#", name); 127 answer = answer.replaceFirst("#name#", name); 128 129 // allow custom name resolution as well. For example with camel-core-osgi we have a custom 130 // name strategy that supports OSGI specific tokens such as #bundleId# etc. 131 answer = customResolveManagementName(pattern, answer); 132 133 // are there any #word# combos left, if so they should be considered invalid tokens 134 if (invalidCheck && INVALID_PATTERN.matcher(answer).matches()) { 135 throw new IllegalArgumentException("Pattern is invalid: " + pattern); 136 } 137 138 return answer; 139 } 140 141 /** 142 * Strategy to do any custom resolution of the name 143 * 144 * @param pattern the pattern 145 * @param answer the current answer, which may have custom patterns still to be resolved 146 * @return the resolved name 147 */ 148 protected String customResolveManagementName(String pattern, String answer) { 149 return answer; 150 } 151 152 private static long nextNameCounter() { 153 // we want to be 1-based, so increment first 154 return NAME_COUNTER.incrementAndGet(); 155 } 156 157 /** 158 * To reset the counter, should only be used for testing purposes. 159 * 160 * @param value the counter value 161 */ 162 public static void setCounter(int value) { 163 NAME_COUNTER.set(value); 164 } 165 166}