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.AbstractMap; 020import java.util.AbstractSet; 021import java.util.ArrayList; 022import java.util.Arrays; 023import java.util.Iterator; 024import java.util.List; 025import java.util.Map; 026import java.util.Set; 027import java.util.concurrent.ConcurrentHashMap; 028 029import org.apache.camel.CamelContext; 030import org.apache.camel.impl.transformer.TransformerKey; 031import org.apache.camel.model.transformer.TransformerDefinition; 032import org.apache.camel.spi.DataType; 033import org.apache.camel.spi.Transformer; 034import org.apache.camel.spi.TransformerRegistry; 035import org.apache.camel.util.CamelContextHelper; 036import org.apache.camel.util.CompoundIterator; 037import org.apache.camel.util.LRUCache; 038import org.apache.camel.util.LRUCacheFactory; 039import org.apache.camel.util.ObjectHelper; 040import org.apache.camel.util.ServiceHelper; 041 042/** 043 * Default implementation of {@link org.apache.camel.spi.TransformerRegistry}. 044 */ 045public class DefaultTransformerRegistry extends AbstractMap<TransformerKey, Transformer> implements TransformerRegistry<TransformerKey> { 046 047 private static final long serialVersionUID = 1L; 048 private Map<TransformerKey, Transformer> dynamicMap; 049 private Map<TransformerKey, Transformer> staticMap; 050 private Map<TransformerKey, TransformerKey> aliasMap; 051 private final CamelContext context; 052 private int maxCacheSize; 053 054 public DefaultTransformerRegistry(CamelContext context) throws Exception { 055 this(context, new ArrayList<>()); 056 } 057 058 public DefaultTransformerRegistry(CamelContext context, List<TransformerDefinition> definitions) throws Exception { 059 this.maxCacheSize = CamelContextHelper.getMaximumTransformerCacheSize(context); 060 // do not stop on eviction, as the transformer may still be in use 061 this.dynamicMap = LRUCacheFactory.newLRUCache(maxCacheSize, maxCacheSize, false); 062 // static map to hold transformers we do not want to be evicted 063 this.staticMap = new ConcurrentHashMap<>(); 064 this.aliasMap = new ConcurrentHashMap<>(); 065 this.context = context; 066 067 for (TransformerDefinition def : definitions) { 068 Transformer transformer = def.createTransformer(context); 069 context.addService(transformer); 070 put(createKey(def), transformer); 071 } 072 } 073 074 @Override 075 public Transformer resolveTransformer(TransformerKey key) { 076 if (ObjectHelper.isEmpty(key.getScheme()) && key.getTo() == null) { 077 return null; 078 } 079 080 // try exact match 081 Transformer answer = get(aliasMap.containsKey(key) ? aliasMap.get(key) : key); 082 if (answer != null || ObjectHelper.isNotEmpty(key.getScheme())) { 083 return answer; 084 } 085 086 // try wildcard match for next - add an alias if matched 087 TransformerKey alias = null; 088 if (key.getFrom() != null && ObjectHelper.isNotEmpty(key.getFrom().getName())) { 089 alias = new TransformerKey(new DataType(key.getFrom().getModel()), key.getTo()); 090 answer = get(alias); 091 } 092 if (answer == null && ObjectHelper.isNotEmpty(key.getTo().getName())) { 093 alias = new TransformerKey(key.getFrom(), new DataType(key.getTo().getModel())); 094 answer = get(alias); 095 } 096 if (answer == null && key.getFrom() != null && ObjectHelper.isNotEmpty(key.getFrom().getName()) 097 && ObjectHelper.isNotEmpty(key.getTo().getName())) { 098 alias = new TransformerKey(new DataType(key.getFrom().getModel()), new DataType(key.getTo().getModel())); 099 answer = get(alias); 100 } 101 if (answer == null && key.getFrom() != null) { 102 alias = new TransformerKey(key.getFrom().getModel()); 103 answer = get(alias); 104 } 105 if (answer == null) { 106 alias = new TransformerKey(key.getTo().getModel()); 107 answer = get(alias); 108 } 109 if (answer != null) { 110 aliasMap.put(key, alias); 111 } 112 113 return answer; 114 } 115 116 @Override 117 public void start() throws Exception { 118 if (dynamicMap instanceof LRUCache) { 119 ((LRUCache) dynamicMap).resetStatistics(); 120 } 121 } 122 123 @Override 124 public Transformer get(Object o) { 125 // try static map first 126 Transformer answer = staticMap.get(o); 127 if (answer == null) { 128 answer = dynamicMap.get(o); 129 } 130 return answer; 131 } 132 133 @Override 134 public Transformer put(TransformerKey key, Transformer transformer) { 135 // at first we must see if the key already exists and then replace it back, so it stays the same spot 136 Transformer answer = staticMap.remove(key); 137 if (answer != null) { 138 // replace existing 139 staticMap.put(key, transformer); 140 return answer; 141 } 142 143 answer = dynamicMap.remove(key); 144 if (answer != null) { 145 // replace existing 146 dynamicMap.put(key, transformer); 147 return answer; 148 } 149 150 // we want transformers to be static if they are part of setting up or starting routes 151 if (context.isSetupRoutes() || context.isStartingRoutes()) { 152 answer = staticMap.put(key, transformer); 153 } else { 154 answer = dynamicMap.put(key, transformer); 155 } 156 157 return answer; 158 } 159 160 @Override 161 public boolean containsKey(Object o) { 162 return staticMap.containsKey(o) || dynamicMap.containsKey(o); 163 } 164 165 @Override 166 public boolean containsValue(Object o) { 167 return staticMap.containsValue(o) || dynamicMap.containsValue(o); 168 } 169 170 @Override 171 public int size() { 172 return staticMap.size() + dynamicMap.size(); 173 } 174 175 public int staticSize() { 176 return staticMap.size(); 177 } 178 179 @Override 180 public int dynamicSize() { 181 return dynamicMap.size(); 182 } 183 184 @Override 185 public boolean isEmpty() { 186 return staticMap.isEmpty() && dynamicMap.isEmpty(); 187 } 188 189 @Override 190 public Transformer remove(Object o) { 191 Transformer answer = staticMap.remove(o); 192 if (answer == null) { 193 answer = dynamicMap.remove(o); 194 } 195 return answer; 196 } 197 198 @Override 199 public void clear() { 200 staticMap.clear(); 201 dynamicMap.clear(); 202 } 203 204 @Override 205 public Set<Entry<TransformerKey, Transformer>> entrySet() { 206 return new AbstractSet<Entry<TransformerKey, Transformer>>() { 207 @Override 208 public Iterator<Entry<TransformerKey, Transformer>> iterator() { 209 return new CompoundIterator<>(Arrays.asList( 210 staticMap.entrySet().iterator(), dynamicMap.entrySet().iterator() 211 )); 212 } 213 214 @Override 215 public int size() { 216 return staticMap.size() + dynamicMap.size(); 217 } 218 }; 219 } 220 221 @Override 222 public int getMaximumCacheSize() { 223 return maxCacheSize; 224 } 225 226 /** 227 * Purges the cache 228 */ 229 @Override 230 public void purge() { 231 // only purge the dynamic part 232 super.clear(); 233 } 234 235 @Override 236 public void cleanUp() { 237 if (dynamicMap instanceof LRUCache) { 238 ((LRUCache) dynamicMap).cleanUp(); 239 } 240 } 241 242 @Override 243 public boolean isStatic(String scheme) { 244 return staticMap.containsKey(new TransformerKey(scheme)); 245 } 246 247 @Override 248 public boolean isStatic(DataType from, DataType to) { 249 return staticMap.containsKey(new TransformerKey(from, to)); 250 } 251 252 @Override 253 public boolean isDynamic(String scheme) { 254 return dynamicMap.containsKey(new TransformerKey(scheme)); 255 } 256 257 @Override 258 public boolean isDynamic(DataType from, DataType to) { 259 return dynamicMap.containsKey(new TransformerKey(from, to)); 260 } 261 262 @Override 263 public void stop() throws Exception { 264 ServiceHelper.stopServices(staticMap.values()); 265 ServiceHelper.stopServices(dynamicMap.values()); 266 purge(); 267 } 268 269 @Override 270 public String toString() { 271 return "TransformerRegistry for " + context.getName() + ", capacity: " + maxCacheSize; 272 } 273 274 private TransformerKey createKey(TransformerDefinition def) { 275 return ObjectHelper.isNotEmpty(def.getScheme()) ? new TransformerKey(def.getScheme()) 276 : new TransformerKey(new DataType(def.getFromType()), new DataType(def.getToType())); 277 } 278 279}