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 */
017 package org.apache.camel.impl;
018
019 import java.util.concurrent.atomic.AtomicLong;
020 import java.util.regex.Matcher;
021 import java.util.regex.Pattern;
022
023 import org.apache.camel.CamelContext;
024 import org.apache.camel.spi.ManagementNameStrategy;
025 import 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 */
033 public 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, "#name#", "#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;
071 }
072 name = resolveManagementName(pattern, camelContext.getName());
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());
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 protected String resolveManagementName(String pattern, String name) {
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 (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 }