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