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}