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.management.JmxSystemPropertyKeys; 025import org.apache.camel.spi.ManagementNameStrategy; 026import org.apache.camel.util.ObjectHelper; 027 028/** 029 * Default implementation of {@link ManagementNameStrategy} 030 * <p/> 031 * This implementation will by default use a name pattern as <tt>#name#</tt> and in case 032 * of a clash, then the pattern will fallback to be using the counter as <tt>#name#-#counter#</tt>. 033 */ 034public class DefaultManagementNameStrategy implements ManagementNameStrategy { 035 036 private static final Pattern INVALID_PATTERN = Pattern.compile(".*#\\w+#.*"); 037 private static final AtomicLong NAME_COUNTER = new AtomicLong(); 038 039 private final CamelContext camelContext; 040 private final String defaultPattern; 041 private final String nextPattern; 042 private String name; 043 private String namePattern; 044 045 public DefaultManagementNameStrategy(CamelContext camelContext) { 046 this(camelContext, null, "#name#-#counter#"); 047 } 048 049 public DefaultManagementNameStrategy(CamelContext camelContext, String defaultPattern, String nextPattern) { 050 this.camelContext = camelContext; 051 this.defaultPattern = defaultPattern; 052 this.nextPattern = nextPattern; 053 } 054 055 @Override 056 public String getNamePattern() { 057 return namePattern; 058 } 059 060 @Override 061 public void setNamePattern(String namePattern) { 062 this.namePattern = namePattern; 063 } 064 065 @Override 066 public String getName() { 067 if (name == null) { 068 String pattern = getNamePattern(); 069 if (pattern == null) { 070 // fallback and use the default pattern which is the same name as the CamelContext has been given 071 pattern = defaultPattern != null ? defaultPattern : camelContext.getManagementStrategy().getManagementAgent().getManagementNamePattern(); 072 } 073 name = resolveManagementName(pattern, camelContext.getName(), true); 074 } 075 return name; 076 } 077 078 @Override 079 public String getNextName() { 080 if (isFixedName()) { 081 // use the fixed name 082 return getName(); 083 } else { 084 // or resolve a new name 085 String pattern = getNamePattern(); 086 if (pattern == null) { 087 // use a pattern that has a counter to ensure unique next name 088 pattern = nextPattern; 089 } 090 return resolveManagementName(pattern, camelContext.getName(), true); 091 } 092 } 093 094 @Override 095 public boolean isFixedName() { 096 // the name will be fixed unless there is a counter token 097 String pattern = getNamePattern(); 098 if (pattern == null) { 099 // we are not fixed by default 100 return false; 101 } 102 return !pattern.contains("#counter#"); 103 } 104 105 /** 106 * Creates a new management name with the given pattern 107 * 108 * @param pattern the pattern 109 * @param name the name 110 * @return the management name 111 * @throws IllegalArgumentException if the pattern or name is invalid or empty 112 */ 113 public String resolveManagementName(String pattern, String name, boolean invalidCheck) { 114 ObjectHelper.notEmpty(pattern, "pattern"); 115 ObjectHelper.notEmpty(name, "name"); 116 117 // must quote the names to have it work as literal replacement 118 name = Matcher.quoteReplacement(name); 119 120 // replace tokens 121 String answer = pattern; 122 if (pattern.contains("#counter#")) { 123 // only increment the counter on-demand 124 answer = pattern.replaceFirst("#counter#", "" + nextNameCounter()); 125 } 126 // camelId and name is the same tokens 127 answer = answer.replaceFirst("#camelId#", name); 128 answer = answer.replaceFirst("#name#", name); 129 130 // allow custom name resolution as well. For example with camel-core-osgi we have a custom 131 // name strategy that supports OSGI specific tokens such as #bundleId# etc. 132 answer = customResolveManagementName(pattern, answer); 133 134 // are there any #word# combos left, if so they should be considered invalid tokens 135 if (invalidCheck && INVALID_PATTERN.matcher(answer).matches()) { 136 throw new IllegalArgumentException("Pattern is invalid: " + pattern); 137 } 138 139 return answer; 140 } 141 142 /** 143 * Strategy to do any custom resolution of the name 144 * 145 * @param pattern the pattern 146 * @param answer the current answer, which may have custom patterns still to be resolved 147 * @return the resolved name 148 */ 149 protected String customResolveManagementName(String pattern, String answer) { 150 return answer; 151 } 152 153 private static long nextNameCounter() { 154 // we want to be 1-based, so increment first 155 return NAME_COUNTER.incrementAndGet(); 156 } 157 158 /** 159 * To reset the counter, should only be used for testing purposes. 160 * 161 * @param value the counter value 162 */ 163 public static void setCounter(int value) { 164 NAME_COUNTER.set(value); 165 } 166 167}