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(), 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    }